@drodil/backstage-plugin-qeta-backend 3.45.1 → 3.45.2
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.
|
@@ -5,17 +5,7 @@ var errors = require('@backstage/errors');
|
|
|
5
5
|
var QetaStore = require('../database/QetaStore.cjs.js');
|
|
6
6
|
var backstagePluginQetaCommon = require('@drodil/backstage-plugin-qeta-common');
|
|
7
7
|
var util = require('./util.cjs.js');
|
|
8
|
-
var DataLoader = require('dataloader');
|
|
9
|
-
var types = require('@backstage/types');
|
|
10
8
|
|
|
11
|
-
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
12
|
-
|
|
13
|
-
var DataLoader__default = /*#__PURE__*/_interopDefaultCompat(DataLoader);
|
|
14
|
-
|
|
15
|
-
const BATCH_SCHEDULE_TIMEOUT = types.durationToMilliseconds({ milliseconds: 20 });
|
|
16
|
-
const LOADER_MAP_EXPIRY = types.durationToMilliseconds({ minutes: 10 });
|
|
17
|
-
const LOADER_CACHE_TIMEOUT = types.durationToMilliseconds({ seconds: 5 });
|
|
18
|
-
const MAX_BATCH_SIZE = 100;
|
|
19
9
|
class PermissionManager {
|
|
20
10
|
constructor(config, auth, httpAuth, userInfo, permissions, auditor) {
|
|
21
11
|
this.config = config;
|
|
@@ -24,11 +14,7 @@ class PermissionManager {
|
|
|
24
14
|
this.userInfo = userInfo;
|
|
25
15
|
this.permissions = permissions;
|
|
26
16
|
this.auditor = auditor;
|
|
27
|
-
this.authorizeLoaderMap = new util.ExpiryMap(LOADER_MAP_EXPIRY);
|
|
28
|
-
this.authorizeConditionalLoaderMap = new util.ExpiryMap(LOADER_MAP_EXPIRY);
|
|
29
17
|
}
|
|
30
|
-
authorizeLoaderMap;
|
|
31
|
-
authorizeConditionalLoaderMap;
|
|
32
18
|
async getCredentials(request, allowServiceToken) {
|
|
33
19
|
const allow = allowServiceToken ? ["user", "service"] : ["user"];
|
|
34
20
|
return await this.httpAuth.credentials(request, { allow });
|
|
@@ -90,11 +76,15 @@ class PermissionManager {
|
|
|
90
76
|
return true;
|
|
91
77
|
}
|
|
92
78
|
if (this.permissions) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
79
|
+
const result = await this.permissions.authorize(
|
|
80
|
+
[
|
|
81
|
+
{
|
|
82
|
+
permission: backstagePluginQetaCommon.qetaModeratePermission
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
{ credentials }
|
|
86
|
+
);
|
|
87
|
+
return result[0].result === pluginPermissionCommon.AuthorizeResult.ALLOW;
|
|
98
88
|
}
|
|
99
89
|
if (!this.auth.isPrincipal(credentials, "user")) {
|
|
100
90
|
return false;
|
|
@@ -192,7 +182,6 @@ class PermissionManager {
|
|
|
192
182
|
throw new errors.NotAllowedError("Unauthorized");
|
|
193
183
|
}
|
|
194
184
|
let decision = { result: pluginPermissionCommon.AuthorizeResult.DENY };
|
|
195
|
-
const loader = this.getAuthorizeLoader(credentials);
|
|
196
185
|
if (pluginPermissionCommon.isResourcePermission(permission)) {
|
|
197
186
|
if (!options?.resource) {
|
|
198
187
|
if (options?.audit) {
|
|
@@ -210,9 +199,16 @@ class PermissionManager {
|
|
|
210
199
|
throw new errors.NotFoundError("Resource not found");
|
|
211
200
|
}
|
|
212
201
|
const resourceRef = this.getResourceRef(options.resource);
|
|
213
|
-
|
|
202
|
+
const result = await this.permissions.authorize(
|
|
203
|
+
[{ permission, resourceRef }],
|
|
204
|
+
{ credentials }
|
|
205
|
+
);
|
|
206
|
+
decision = result[0];
|
|
214
207
|
} else {
|
|
215
|
-
|
|
208
|
+
const result = await this.permissions.authorize([{ permission }], {
|
|
209
|
+
credentials
|
|
210
|
+
});
|
|
211
|
+
decision = result[0];
|
|
216
212
|
}
|
|
217
213
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
218
214
|
if (options?.audit) {
|
|
@@ -257,8 +253,11 @@ class PermissionManager {
|
|
|
257
253
|
}
|
|
258
254
|
let decision = { result: pluginPermissionCommon.AuthorizeResult.DENY };
|
|
259
255
|
if (pluginPermissionCommon.isResourcePermission(permission)) {
|
|
260
|
-
const
|
|
261
|
-
|
|
256
|
+
const result = await this.permissions.authorizeConditional(
|
|
257
|
+
[{ permission }],
|
|
258
|
+
{ credentials }
|
|
259
|
+
);
|
|
260
|
+
decision = result[0];
|
|
262
261
|
}
|
|
263
262
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
264
263
|
this.auditor?.createEvent({
|
|
@@ -299,64 +298,6 @@ class PermissionManager {
|
|
|
299
298
|
}
|
|
300
299
|
return void 0;
|
|
301
300
|
}
|
|
302
|
-
getLoaderKey(credentials) {
|
|
303
|
-
if (this.auth.isPrincipal(credentials, "user")) {
|
|
304
|
-
return btoa(`user.${credentials.principal.userEntityRef}`);
|
|
305
|
-
} else if (this.auth.isPrincipal(credentials, "service")) {
|
|
306
|
-
return btoa(`service.${credentials.principal.subject}`);
|
|
307
|
-
} else if (this.auth.isPrincipal(credentials, "none")) {
|
|
308
|
-
return btoa("none");
|
|
309
|
-
}
|
|
310
|
-
return btoa("unknown");
|
|
311
|
-
}
|
|
312
|
-
getAuthorizeLoader(credentials) {
|
|
313
|
-
const key = this.getLoaderKey(credentials);
|
|
314
|
-
const loader = this.authorizeLoaderMap.get(key);
|
|
315
|
-
if (loader) {
|
|
316
|
-
return loader;
|
|
317
|
-
}
|
|
318
|
-
const newLoader = new DataLoader__default.default(
|
|
319
|
-
async (requests) => {
|
|
320
|
-
return await this.permissions.authorize([...requests], {
|
|
321
|
-
credentials
|
|
322
|
-
});
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
cacheMap: new util.ExpiryMap(LOADER_CACHE_TIMEOUT),
|
|
326
|
-
maxBatchSize: MAX_BATCH_SIZE,
|
|
327
|
-
batchScheduleFn: (cb) => setTimeout(cb, BATCH_SCHEDULE_TIMEOUT),
|
|
328
|
-
cacheKeyFn: (req) => {
|
|
329
|
-
return btoa(`${key}-${JSON.stringify(req)}`);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
);
|
|
333
|
-
this.authorizeLoaderMap.set(key, newLoader);
|
|
334
|
-
return newLoader;
|
|
335
|
-
}
|
|
336
|
-
getAuthorizeConditionalLoader(credentials) {
|
|
337
|
-
const key = this.getLoaderKey(credentials);
|
|
338
|
-
const loader = this.authorizeConditionalLoaderMap.get(key);
|
|
339
|
-
if (loader) {
|
|
340
|
-
return loader;
|
|
341
|
-
}
|
|
342
|
-
const newLoader = new DataLoader__default.default(
|
|
343
|
-
async (requests) => {
|
|
344
|
-
return await this.permissions.authorizeConditional([...requests], {
|
|
345
|
-
credentials
|
|
346
|
-
});
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
cacheMap: new util.ExpiryMap(LOADER_CACHE_TIMEOUT),
|
|
350
|
-
maxBatchSize: MAX_BATCH_SIZE,
|
|
351
|
-
batchScheduleFn: (cb) => setTimeout(cb, BATCH_SCHEDULE_TIMEOUT),
|
|
352
|
-
cacheKeyFn: (req) => {
|
|
353
|
-
return btoa(`${key}-${JSON.stringify(req)}`);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
);
|
|
357
|
-
this.authorizeConditionalLoaderMap.set(key, newLoader);
|
|
358
|
-
return newLoader;
|
|
359
|
-
}
|
|
360
301
|
getResourceRef(resource) {
|
|
361
302
|
if (QetaStore.isPost(resource)) {
|
|
362
303
|
return `qeta:post:${resource.id}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionManager.cjs.js","sources":["../../src/service/PermissionManager.ts"],"sourcesContent":["import { Request } from 'express';\nimport {\n AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n AuthorizeResult,\n DefinitivePolicyDecision,\n isCreatePermission,\n isPermission,\n isReadPermission,\n isResourcePermission,\n isUpdatePermission,\n Permission,\n PermissionCriteria,\n PolicyDecision,\n QueryPermissionRequest,\n QueryPermissionResponse,\n} from '@backstage/plugin-permission-common';\nimport {\n AuthenticationError,\n NotAllowedError,\n NotFoundError,\n} from '@backstage/errors';\nimport {\n AuditorService,\n AuthService,\n BackstageCredentials,\n BackstagePrincipalTypes,\n HttpAuthService,\n PermissionsService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport {\n isAnswer,\n isCollection,\n isComment,\n isPost,\n isTag,\n} from '../database/QetaStore.ts';\nimport {\n qetaCreateTagPermission,\n QetaIdEntity,\n qetaModeratePermission,\n} from '@drodil/backstage-plugin-qeta-common';\nimport { Config } from '@backstage/config';\nimport { ExpiryMap, QetaFilters, transformConditions } from './util.ts';\nimport DataLoader from 'dataloader';\nimport { durationToMilliseconds } from '@backstage/types';\n\nconst BATCH_SCHEDULE_TIMEOUT = durationToMilliseconds({ milliseconds: 20 });\nconst LOADER_MAP_EXPIRY = durationToMilliseconds({ minutes: 10 });\nconst LOADER_CACHE_TIMEOUT = durationToMilliseconds({ seconds: 5 });\nconst MAX_BATCH_SIZE = 100;\n\nexport class PermissionManager {\n private readonly authorizeLoaderMap: ExpiryMap<\n string,\n DataLoader<AuthorizePermissionRequest, AuthorizePermissionResponse>\n >;\n private readonly authorizeConditionalLoaderMap: ExpiryMap<\n string,\n DataLoader<QueryPermissionRequest, QueryPermissionResponse>\n >;\n\n constructor(\n private readonly config: Config,\n private readonly auth: AuthService,\n private readonly httpAuth: HttpAuthService,\n private readonly userInfo: UserInfoService,\n private readonly permissions?: PermissionsService,\n private readonly auditor?: AuditorService,\n ) {\n this.authorizeLoaderMap = new ExpiryMap(LOADER_MAP_EXPIRY);\n this.authorizeConditionalLoaderMap = new ExpiryMap(LOADER_MAP_EXPIRY);\n }\n\n public async getCredentials(\n request: Request<unknown>,\n allowServiceToken?: boolean,\n ) {\n const allow: Array<keyof BackstagePrincipalTypes> = allowServiceToken\n ? ['user', 'service']\n : ['user'];\n return await this.httpAuth.credentials(request, { allow });\n }\n\n public async getUsername(\n req: Request<unknown>,\n allowServiceToken?: boolean,\n creds?: BackstageCredentials,\n ): Promise<string> {\n const allowMetadataInput = this.config.getOptionalBoolean(\n 'qeta.allowMetadataInput',\n );\n\n if (allowMetadataInput && req.body.user) {\n return req.body.user;\n } else if (allowMetadataInput && req.get('x-qeta-user')) {\n return req.get('x-qeta-user')!;\n }\n\n try {\n if (creds && this.auth.isPrincipal(creds, 'user')) {\n return creds.principal.userEntityRef;\n }\n\n const credentials = await this.httpAuth.credentials(req, {\n allow: ['user'],\n });\n if (credentials) {\n return credentials.principal.userEntityRef;\n }\n } catch (_) {\n // NOOP\n }\n\n if (allowServiceToken) {\n try {\n if (creds && this.auth.isPrincipal(creds, 'service')) {\n return creds.principal.subject;\n }\n\n const credentials = await this.httpAuth.credentials(req, {\n allow: ['service'],\n });\n if (credentials) {\n return credentials.principal.subject;\n }\n } catch (_) {\n // NOOP\n }\n }\n\n const allowAnonymous = this.config.getOptionalBoolean(\n 'qeta.allowAnonymous',\n );\n\n if (allowAnonymous) {\n return 'user:default/guest';\n }\n\n throw new AuthenticationError(\n `Missing or invalid token in 'authorization' header`,\n );\n }\n\n public async isModerator(\n req: Request<unknown>,\n options?: {\n credentials?: BackstageCredentials;\n },\n ): Promise<boolean> {\n try {\n const credentials =\n options?.credentials ??\n (await this.httpAuth.credentials(req, {\n allow: ['user', 'service'],\n }));\n if (!credentials) {\n return false;\n }\n\n // Service tokens are always moderators\n if (this.auth.isPrincipal(credentials, 'service')) {\n return true;\n }\n\n // Authorize moderator using permission framework\n if (this.permissions) {\n const loader = this.getAuthorizeLoader(credentials);\n const result = await loader.load({\n permission: qetaModeratePermission,\n });\n return result.result === AuthorizeResult.ALLOW;\n }\n\n if (!this.auth.isPrincipal(credentials, 'user')) {\n return false;\n }\n\n // Authorize moderator using config\n const username = credentials.principal.userEntityRef;\n const user = await this.userInfo.getUserInfo(credentials);\n\n const ownership: string[] = user?.ownershipEntityRefs ?? [];\n ownership.push(username);\n\n const moderators =\n this.config.getOptionalStringArray('qeta.moderators') ?? [];\n return moderators.some((m: string) => ownership.includes(m));\n } catch (_) {\n return false;\n }\n }\n\n public async authorizeWithoutPermissions(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n resource?: QetaIdEntity | null;\n audit?: boolean;\n credentials?: BackstageCredentials;\n },\n ): Promise<DefinitivePolicyDecision> {\n const readPermission = isReadPermission(permission);\n const createPermission = isCreatePermission(permission);\n\n if (isPermission(permission, qetaCreateTagPermission)) {\n return this.config.getOptionalBoolean('qeta.tags.allowCreation') ?? true\n ? { result: AuthorizeResult.ALLOW }\n : { result: AuthorizeResult.DENY };\n }\n\n if (readPermission || createPermission) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n if (!options?.resource) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n resource: JSON.stringify(options?.resource),\n failure: 'Resource not found',\n },\n });\n }\n throw new NotFoundError('Resource not found');\n }\n\n const username = await this.getUsername(request, true, options.credentials);\n\n // Experts can edit and delete\n if (\n 'experts' in options.resource &&\n Array.isArray(options.resource.experts) &&\n options.resource.experts.includes(username)\n ) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n const moderator = await this.isModerator(request, options);\n if (moderator) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n const globalEdit =\n this.config.getOptionalBoolean('qeta.allowGlobalEdits') ?? false;\n const editPermission = isUpdatePermission(permission);\n\n if (globalEdit && editPermission) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n if ('author' in options.resource && username === options.resource.author) {\n return { result: AuthorizeResult.ALLOW };\n } else if (\n 'owner' in options.resource &&\n username === options.resource.owner\n ) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n if (options.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n }\n\n return { result: AuthorizeResult.DENY };\n }\n\n public async authorize(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n resource?: QetaIdEntity | null;\n audit?: boolean;\n credentials?: BackstageCredentials;\n },\n ): Promise<DefinitivePolicyDecision> {\n if (!this.permissions) {\n return await this.authorizeWithoutPermissions(\n request,\n permission,\n options,\n );\n }\n\n const moderator = await this.isModerator(request, options);\n\n if (moderator) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n const credentials =\n options?.credentials ?? (await this.httpAuth.credentials(request));\n if (!credentials) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n }\n throw new NotAllowedError('Unauthorized');\n }\n\n let decision: DefinitivePolicyDecision = { result: AuthorizeResult.DENY };\n const loader = this.getAuthorizeLoader(credentials);\n if (isResourcePermission(permission)) {\n if (!options?.resource) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n resource: JSON.stringify(options.resource),\n failure: 'Resource not found',\n },\n });\n }\n throw new NotFoundError('Resource not found');\n }\n\n const resourceRef = this.getResourceRef(options.resource);\n\n decision = await loader.load({ permission, resourceRef });\n } else {\n decision = await loader.load({ permission });\n }\n\n if (decision.result === AuthorizeResult.DENY) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n }\n throw new NotAllowedError('Unauthorized');\n }\n return decision;\n }\n\n public async authorizeConditional(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n allowServicePrincipal?: boolean;\n creds?: BackstageCredentials;\n },\n ): Promise<PolicyDecision> {\n if (!this.permissions) {\n return await this.authorizeWithoutPermissions(request, permission, {\n resource: null,\n audit: true,\n ...options,\n });\n }\n\n const allow: Array<keyof BackstagePrincipalTypes> =\n options?.allowServicePrincipal ? ['user', 'service'] : ['user'];\n const credentials =\n options?.creds ?? (await this.httpAuth.credentials(request, { allow }));\n\n if (!credentials) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n throw new NotAllowedError('Unauthorized');\n }\n\n if (\n options?.allowServicePrincipal &&\n this.auth.isPrincipal(credentials, 'service')\n ) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n let decision: PolicyDecision = { result: AuthorizeResult.DENY };\n if (isResourcePermission(permission)) {\n const loader = this.getAuthorizeConditionalLoader(credentials);\n decision = await loader.load({ permission });\n }\n\n if (decision.result === AuthorizeResult.DENY) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n throw new NotAllowedError('Unauthorized');\n }\n return decision;\n }\n\n public async authorizeBoolean(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n resource?: QetaIdEntity | null;\n credentials?: BackstageCredentials;\n },\n ): Promise<boolean> {\n try {\n const res = await this.authorize(request, permission, {\n audit: false,\n ...options,\n });\n return res.result === AuthorizeResult.ALLOW;\n } catch (e) {\n return false;\n }\n }\n\n public async getAuthorizeConditions(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n allowServicePrincipal?: boolean;\n creds?: BackstageCredentials;\n },\n ): Promise<PermissionCriteria<QetaFilters> | undefined> {\n if (!this.permissions) {\n return undefined;\n }\n\n const decision = await this.authorizeConditional(\n request,\n permission,\n options,\n );\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n return transformConditions(decision.conditions);\n }\n return undefined;\n }\n\n private getLoaderKey(credentials: BackstageCredentials) {\n if (this.auth.isPrincipal(credentials, 'user')) {\n return btoa(`user.${credentials.principal.userEntityRef}`);\n } else if (this.auth.isPrincipal(credentials, 'service')) {\n return btoa(`service.${credentials.principal.subject}`);\n } else if (this.auth.isPrincipal(credentials, 'none')) {\n return btoa('none');\n }\n return btoa('unknown');\n }\n\n private getAuthorizeLoader(credentials: BackstageCredentials) {\n const key = this.getLoaderKey(credentials);\n const loader = this.authorizeLoaderMap.get(key);\n if (loader) {\n return loader;\n }\n const newLoader = new DataLoader<\n AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n string\n >(\n async requests => {\n return await this.permissions!.authorize([...requests], {\n credentials,\n });\n },\n {\n cacheMap: new ExpiryMap(LOADER_CACHE_TIMEOUT),\n maxBatchSize: MAX_BATCH_SIZE,\n batchScheduleFn: cb => setTimeout(cb, BATCH_SCHEDULE_TIMEOUT),\n cacheKeyFn: req => {\n return btoa(`${key}-${JSON.stringify(req)}`);\n },\n },\n );\n this.authorizeLoaderMap.set(key, newLoader);\n return newLoader;\n }\n\n private getAuthorizeConditionalLoader(credentials: BackstageCredentials) {\n const key = this.getLoaderKey(credentials);\n const loader = this.authorizeConditionalLoaderMap.get(key);\n if (loader) {\n return loader;\n }\n\n const newLoader = new DataLoader<\n QueryPermissionRequest,\n QueryPermissionResponse,\n string\n >(\n async requests => {\n return await this.permissions!.authorizeConditional([...requests], {\n credentials,\n });\n },\n {\n cacheMap: new ExpiryMap(LOADER_CACHE_TIMEOUT),\n maxBatchSize: MAX_BATCH_SIZE,\n batchScheduleFn: cb => setTimeout(cb, BATCH_SCHEDULE_TIMEOUT),\n cacheKeyFn: req => {\n return btoa(`${key}-${JSON.stringify(req)}`);\n },\n },\n );\n this.authorizeConditionalLoaderMap.set(key, newLoader);\n return newLoader;\n }\n\n private getResourceRef(resource: QetaIdEntity) {\n if (isPost(resource)) {\n return `qeta:post:${resource.id}`;\n }\n if (isAnswer(resource)) {\n return `qeta:answer:${resource.id}`;\n }\n if (isTag(resource)) {\n return `qeta:tag:${resource.id}`;\n }\n if (isComment(resource)) {\n return `qeta:comment:${resource.id}`;\n }\n if (isCollection(resource)) {\n return `qeta:collection:${resource.id}`;\n }\n throw new Error('Invalid resource type');\n }\n}\n"],"names":["durationToMilliseconds","ExpiryMap","AuthenticationError","qetaModeratePermission","AuthorizeResult","isReadPermission","isCreatePermission","isPermission","qetaCreateTagPermission","NotFoundError","isUpdatePermission","NotAllowedError","isResourcePermission","transformConditions","DataLoader","isPost","isAnswer","isTag","isComment","isCollection"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PermissionManager.cjs.js","sources":["../../src/service/PermissionManager.ts"],"sourcesContent":["import { Request } from 'express';\nimport {\n AuthorizeResult,\n DefinitivePolicyDecision,\n isCreatePermission,\n isPermission,\n isReadPermission,\n isResourcePermission,\n isUpdatePermission,\n Permission,\n PermissionCriteria,\n PolicyDecision,\n} from '@backstage/plugin-permission-common';\nimport {\n AuthenticationError,\n NotAllowedError,\n NotFoundError,\n} from '@backstage/errors';\nimport {\n AuditorService,\n AuthService,\n BackstageCredentials,\n BackstagePrincipalTypes,\n HttpAuthService,\n PermissionsService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport {\n isAnswer,\n isCollection,\n isComment,\n isPost,\n isTag,\n} from '../database/QetaStore.ts';\nimport {\n qetaCreateTagPermission,\n QetaIdEntity,\n qetaModeratePermission,\n} from '@drodil/backstage-plugin-qeta-common';\nimport { Config } from '@backstage/config';\nimport { QetaFilters, transformConditions } from './util.ts';\n\nexport class PermissionManager {\n constructor(\n private readonly config: Config,\n private readonly auth: AuthService,\n private readonly httpAuth: HttpAuthService,\n private readonly userInfo: UserInfoService,\n private readonly permissions?: PermissionsService,\n private readonly auditor?: AuditorService,\n ) {}\n\n public async getCredentials(\n request: Request<unknown>,\n allowServiceToken?: boolean,\n ) {\n const allow: Array<keyof BackstagePrincipalTypes> = allowServiceToken\n ? ['user', 'service']\n : ['user'];\n return await this.httpAuth.credentials(request, { allow });\n }\n\n public async getUsername(\n req: Request<unknown>,\n allowServiceToken?: boolean,\n creds?: BackstageCredentials,\n ): Promise<string> {\n const allowMetadataInput = this.config.getOptionalBoolean(\n 'qeta.allowMetadataInput',\n );\n\n if (allowMetadataInput && req.body.user) {\n return req.body.user;\n } else if (allowMetadataInput && req.get('x-qeta-user')) {\n return req.get('x-qeta-user')!;\n }\n\n try {\n if (creds && this.auth.isPrincipal(creds, 'user')) {\n return creds.principal.userEntityRef;\n }\n\n const credentials = await this.httpAuth.credentials(req, {\n allow: ['user'],\n });\n if (credentials) {\n return credentials.principal.userEntityRef;\n }\n } catch (_) {\n // NOOP\n }\n\n if (allowServiceToken) {\n try {\n if (creds && this.auth.isPrincipal(creds, 'service')) {\n return creds.principal.subject;\n }\n\n const credentials = await this.httpAuth.credentials(req, {\n allow: ['service'],\n });\n if (credentials) {\n return credentials.principal.subject;\n }\n } catch (_) {\n // NOOP\n }\n }\n\n const allowAnonymous = this.config.getOptionalBoolean(\n 'qeta.allowAnonymous',\n );\n\n if (allowAnonymous) {\n return 'user:default/guest';\n }\n\n throw new AuthenticationError(\n `Missing or invalid token in 'authorization' header`,\n );\n }\n\n public async isModerator(\n req: Request<unknown>,\n options?: {\n credentials?: BackstageCredentials;\n },\n ): Promise<boolean> {\n try {\n const credentials =\n options?.credentials ??\n (await this.httpAuth.credentials(req, {\n allow: ['user', 'service'],\n }));\n if (!credentials) {\n return false;\n }\n\n // Service tokens are always moderators\n if (this.auth.isPrincipal(credentials, 'service')) {\n return true;\n }\n\n // Authorize moderator using permission framework\n if (this.permissions) {\n const result = await this.permissions.authorize(\n [\n {\n permission: qetaModeratePermission,\n },\n ],\n { credentials },\n );\n return result[0].result === AuthorizeResult.ALLOW;\n }\n\n if (!this.auth.isPrincipal(credentials, 'user')) {\n return false;\n }\n\n // Authorize moderator using config\n const username = credentials.principal.userEntityRef;\n const user = await this.userInfo.getUserInfo(credentials);\n\n const ownership: string[] = user?.ownershipEntityRefs ?? [];\n ownership.push(username);\n\n const moderators =\n this.config.getOptionalStringArray('qeta.moderators') ?? [];\n return moderators.some((m: string) => ownership.includes(m));\n } catch (_) {\n return false;\n }\n }\n\n public async authorizeWithoutPermissions(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n resource?: QetaIdEntity | null;\n audit?: boolean;\n credentials?: BackstageCredentials;\n },\n ): Promise<DefinitivePolicyDecision> {\n const readPermission = isReadPermission(permission);\n const createPermission = isCreatePermission(permission);\n\n if (isPermission(permission, qetaCreateTagPermission)) {\n return this.config.getOptionalBoolean('qeta.tags.allowCreation') ?? true\n ? { result: AuthorizeResult.ALLOW }\n : { result: AuthorizeResult.DENY };\n }\n\n if (readPermission || createPermission) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n if (!options?.resource) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n resource: JSON.stringify(options?.resource),\n failure: 'Resource not found',\n },\n });\n }\n throw new NotFoundError('Resource not found');\n }\n\n const username = await this.getUsername(request, true, options.credentials);\n\n // Experts can edit and delete\n if (\n 'experts' in options.resource &&\n Array.isArray(options.resource.experts) &&\n options.resource.experts.includes(username)\n ) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n const moderator = await this.isModerator(request, options);\n if (moderator) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n const globalEdit =\n this.config.getOptionalBoolean('qeta.allowGlobalEdits') ?? false;\n const editPermission = isUpdatePermission(permission);\n\n if (globalEdit && editPermission) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n if ('author' in options.resource && username === options.resource.author) {\n return { result: AuthorizeResult.ALLOW };\n } else if (\n 'owner' in options.resource &&\n username === options.resource.owner\n ) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n if (options.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n }\n\n return { result: AuthorizeResult.DENY };\n }\n\n public async authorize(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n resource?: QetaIdEntity | null;\n audit?: boolean;\n credentials?: BackstageCredentials;\n },\n ): Promise<DefinitivePolicyDecision> {\n if (!this.permissions) {\n return await this.authorizeWithoutPermissions(\n request,\n permission,\n options,\n );\n }\n\n const moderator = await this.isModerator(request, options);\n\n if (moderator) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n const credentials =\n options?.credentials ?? (await this.httpAuth.credentials(request));\n if (!credentials) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n }\n throw new NotAllowedError('Unauthorized');\n }\n\n let decision: DefinitivePolicyDecision = { result: AuthorizeResult.DENY };\n if (isResourcePermission(permission)) {\n if (!options?.resource) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n resource: JSON.stringify(options.resource),\n failure: 'Resource not found',\n },\n });\n }\n throw new NotFoundError('Resource not found');\n }\n\n const resourceRef = this.getResourceRef(options.resource);\n const result = await this.permissions.authorize(\n [{ permission, resourceRef }],\n { credentials },\n );\n decision = result[0];\n } else {\n const result = await this.permissions.authorize([{ permission }], {\n credentials,\n });\n decision = result[0];\n }\n\n if (decision.result === AuthorizeResult.DENY) {\n if (options?.audit) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n }\n throw new NotAllowedError('Unauthorized');\n }\n return decision;\n }\n\n public async authorizeConditional(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n allowServicePrincipal?: boolean;\n creds?: BackstageCredentials;\n },\n ): Promise<PolicyDecision> {\n if (!this.permissions) {\n return await this.authorizeWithoutPermissions(request, permission, {\n resource: null,\n audit: true,\n ...options,\n });\n }\n\n const allow: Array<keyof BackstagePrincipalTypes> =\n options?.allowServicePrincipal ? ['user', 'service'] : ['user'];\n const credentials =\n options?.creds ?? (await this.httpAuth.credentials(request, { allow }));\n\n if (!credentials) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n throw new NotAllowedError('Unauthorized');\n }\n\n if (\n options?.allowServicePrincipal &&\n this.auth.isPrincipal(credentials, 'service')\n ) {\n return { result: AuthorizeResult.ALLOW };\n }\n\n let decision: PolicyDecision = { result: AuthorizeResult.DENY };\n if (isResourcePermission(permission)) {\n const result = await this.permissions.authorizeConditional(\n [{ permission }],\n { credentials },\n );\n decision = result[0];\n }\n\n if (decision.result === AuthorizeResult.DENY) {\n this.auditor?.createEvent({\n eventId: 'authorize',\n severityLevel: 'high',\n request,\n meta: {\n permission,\n failure: 'Unauthorized',\n },\n });\n throw new NotAllowedError('Unauthorized');\n }\n return decision;\n }\n\n public async authorizeBoolean(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n resource?: QetaIdEntity | null;\n credentials?: BackstageCredentials;\n },\n ): Promise<boolean> {\n try {\n const res = await this.authorize(request, permission, {\n audit: false,\n ...options,\n });\n return res.result === AuthorizeResult.ALLOW;\n } catch (e) {\n return false;\n }\n }\n\n public async getAuthorizeConditions(\n request: Request<unknown>,\n permission: Permission,\n options?: {\n allowServicePrincipal?: boolean;\n creds?: BackstageCredentials;\n },\n ): Promise<PermissionCriteria<QetaFilters> | undefined> {\n if (!this.permissions) {\n return undefined;\n }\n\n const decision = await this.authorizeConditional(\n request,\n permission,\n options,\n );\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n return transformConditions(decision.conditions);\n }\n return undefined;\n }\n\n private getResourceRef(resource: QetaIdEntity) {\n if (isPost(resource)) {\n return `qeta:post:${resource.id}`;\n }\n if (isAnswer(resource)) {\n return `qeta:answer:${resource.id}`;\n }\n if (isTag(resource)) {\n return `qeta:tag:${resource.id}`;\n }\n if (isComment(resource)) {\n return `qeta:comment:${resource.id}`;\n }\n if (isCollection(resource)) {\n return `qeta:collection:${resource.id}`;\n }\n throw new Error('Invalid resource type');\n }\n}\n"],"names":["AuthenticationError","qetaModeratePermission","AuthorizeResult","isReadPermission","isCreatePermission","isPermission","qetaCreateTagPermission","NotFoundError","isUpdatePermission","NotAllowedError","isResourcePermission","transformConditions","isPost","isAnswer","isTag","isComment","isCollection"],"mappings":"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"}
|
package/dist/service/util.cjs.js
CHANGED
|
@@ -310,46 +310,7 @@ const getAzureBlobServiceClient = (config) => {
|
|
|
310
310
|
"Either account name or connection string must be provided for Azure Blob Storage"
|
|
311
311
|
);
|
|
312
312
|
};
|
|
313
|
-
class ExpiryMap extends Map {
|
|
314
|
-
#ttlMs;
|
|
315
|
-
#timestamps = /* @__PURE__ */ new Map();
|
|
316
|
-
constructor(ttlMs) {
|
|
317
|
-
super();
|
|
318
|
-
this.#ttlMs = ttlMs;
|
|
319
|
-
}
|
|
320
|
-
set(key, value) {
|
|
321
|
-
const result = super.set(key, value);
|
|
322
|
-
this.#timestamps.set(key, (/* @__PURE__ */ new Date()).getTime());
|
|
323
|
-
this.clearOld();
|
|
324
|
-
return result;
|
|
325
|
-
}
|
|
326
|
-
get(key) {
|
|
327
|
-
this.clearOld();
|
|
328
|
-
if (!this.has(key)) {
|
|
329
|
-
return void 0;
|
|
330
|
-
}
|
|
331
|
-
return super.get(key);
|
|
332
|
-
}
|
|
333
|
-
delete(key) {
|
|
334
|
-
this.#timestamps.delete(key);
|
|
335
|
-
return super.delete(key);
|
|
336
|
-
}
|
|
337
|
-
clear() {
|
|
338
|
-
this.#timestamps.clear();
|
|
339
|
-
return super.clear();
|
|
340
|
-
}
|
|
341
|
-
clearOld() {
|
|
342
|
-
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
343
|
-
this.#timestamps.forEach((val, key) => {
|
|
344
|
-
if (now - val > this.#ttlMs) {
|
|
345
|
-
this.#timestamps.delete(key);
|
|
346
|
-
super.delete(key);
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
313
|
|
|
352
|
-
exports.ExpiryMap = ExpiryMap;
|
|
353
314
|
exports.getAzureBlobServiceClient = getAzureBlobServiceClient;
|
|
354
315
|
exports.getCreated = getCreated;
|
|
355
316
|
exports.getS3Client = getS3Client;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.cjs.js","sources":["../../src/service/util.ts"],"sourcesContent":["import { Request } from 'express';\nimport { format, subDays } from 'date-fns';\nimport {\n isAnswer,\n isCollection,\n isPost,\n isTag,\n MaybeAnswer,\n MaybeCollection,\n MaybePost,\n MaybeTag,\n} from '../database/QetaStore';\nimport { Config } from '@backstage/config';\nimport { S3Client } from '@aws-sdk/client-s3';\nimport { RouteOptions } from './types';\nimport {\n Answer,\n AnswerResponse,\n CollectionResponse,\n Comment,\n PostResponse,\n qetaDeleteAnswerPermission,\n qetaDeleteCollectionPermission,\n qetaDeleteCommentPermission,\n qetaDeletePostPermission,\n qetaDeleteTagPermission,\n qetaEditAnswerPermission,\n qetaEditCollectionPermission,\n qetaEditCommentPermission,\n qetaEditPostPermission,\n qetaEditTagPermission,\n TagResponse,\n} from '@drodil/backstage-plugin-qeta-common';\nimport { NodeHttpHandler } from '@smithy/node-http-handler';\nimport { HttpsProxyAgent } from 'hpagent';\nimport { compact } from 'lodash';\nimport {\n ConditionTransformer,\n createConditionTransformer,\n} from '@backstage/plugin-permission-node';\nimport { rules } from '@drodil/backstage-plugin-qeta-node';\nimport { BlobServiceClient } from '@azure/storage-blob';\nimport { DefaultAzureCredential } from '@azure/identity';\nimport { BackstageCredentials } from '@backstage/backend-plugin-api';\nimport { PermissionManager } from './PermissionManager.ts';\n\nexport const getCreated = async (\n req: Request<unknown>,\n options: RouteOptions,\n): Promise<Date> => {\n const allowMetadataInput = options.config.getOptionalBoolean(\n 'qeta.allowMetadataInput',\n );\n\n if (allowMetadataInput && req.body.created) {\n return new Date(req.body.created);\n }\n\n return new Date();\n};\n\nexport type QetaFilter = {\n property:\n | 'posts.id'\n | 'posts.author'\n | 'posts.type'\n | 'tags'\n | 'entityRefs'\n | 'answers.id'\n | 'answers.author'\n | 'comments.id'\n | 'comments.author'\n | 'tags.tag'\n | 'tag.experts'\n | 'collections.owner'\n | 'collections.id';\n values: Array<string | undefined>;\n};\n\nexport type QetaFilters =\n | { anyOf: QetaFilter[] }\n | { allOf: QetaFilter[] }\n | { not: QetaFilter }\n | QetaFilter;\n\nexport const transformConditions: ConditionTransformer<QetaFilters> =\n createConditionTransformer(Object.values(rules));\n\nconst mapTagAdditionalFields = async (\n request: Request<unknown>,\n resource: TagResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n checkRights?: boolean,\n) => {\n const [canEdit, canDelete] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditTagPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaDeleteTagPermission, {\n resource,\n credentials,\n })\n : undefined,\n ]);\n\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n return resource;\n};\n\nconst mapCollectionAdditionalFields = async (\n request: Request<unknown>,\n resource: CollectionResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n checkRights?: boolean,\n) => {\n const [canEdit, canDelete] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditCollectionPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(\n request,\n qetaDeleteCollectionPermission,\n {\n resource,\n credentials,\n },\n )\n : undefined,\n ]);\n\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n return resource;\n};\n\nconst mapResourceComments = async (\n request: Request<unknown>,\n resource: AnswerResponse | PostResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n const commentArr = resource.comments ?? [];\n const editPermissions = await Promise.all(\n commentArr.map(async (c: Comment) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(\n request,\n qetaEditCommentPermission,\n { resource: c, credentials },\n );\n }),\n );\n\n const deletePermissions = await Promise.all(\n commentArr.map(async (c: Comment) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(\n request,\n qetaDeleteCommentPermission,\n { resource: c, credentials },\n );\n }),\n );\n\n const comments: (Comment | null)[] = commentArr.map(\n (c: Comment, index): Comment => {\n return {\n ...c,\n own: c.author === username,\n expert: c.experts?.includes(c.author),\n canEdit: editPermissions[index],\n canDelete: deletePermissions[index],\n };\n },\n );\n return compact(comments);\n};\n\nconst mapAnswerAdditionalFields = async (\n request: Request<unknown>,\n resource: AnswerResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n const [canEdit, canDelete, comments] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditAnswerPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaDeleteAnswerPermission, {\n resource,\n credentials,\n })\n : undefined,\n mapResourceComments(\n request,\n resource,\n permissionMgr,\n credentials,\n username,\n checkRights,\n ),\n ]);\n\n resource.ownVote = resource.votes?.find(v => v.author === username)?.score;\n resource.own = resource.author === username;\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n resource.expert = resource.experts?.includes(resource.author);\n resource.comments = comments;\n\n return resource;\n};\n\nconst mapPostAnswers = async (\n request: Request<unknown>,\n resource: PostResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n const answersArray = resource.answers ?? [];\n const editPermissions = await Promise.all(\n answersArray.map(async (a: Answer) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(request, qetaEditAnswerPermission, {\n resource: a,\n credentials,\n });\n }),\n );\n\n const deletePermissions = await Promise.all(\n answersArray.map(async (a: Answer) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(\n request,\n qetaDeleteAnswerPermission,\n { resource: a, credentials },\n );\n }),\n );\n\n const comments = await Promise.all(\n answersArray.map(async (a: Answer) => {\n return mapResourceComments(\n request,\n a,\n permissionMgr,\n credentials,\n username,\n checkRights,\n );\n }),\n );\n\n return answersArray.map((a: Answer, index: number) => {\n return {\n ...a,\n ownVote: a.votes?.find(v => v.author === username)?.score,\n own: resource.author === username,\n canEdit: editPermissions[index],\n canDelete: deletePermissions[index],\n expert: a.experts?.includes(resource.author),\n comments: comments[index],\n };\n });\n};\n\nconst mapPostAdditionalFields = async (\n request: Request<unknown>,\n resource: PostResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n resource.ownVote = resource.votes?.find(v => v.author === username)?.score;\n resource.own = resource.author === username;\n\n const [canEdit, canDelete, answers, comments] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditPostPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaDeletePostPermission, {\n resource,\n credentials,\n })\n : undefined,\n mapPostAnswers(\n request,\n resource,\n permissionMgr,\n credentials,\n username,\n checkRights,\n ),\n mapResourceComments(\n request,\n resource,\n permissionMgr,\n credentials,\n username,\n checkRights,\n ),\n ]);\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n resource.answers = answers;\n resource.comments = comments;\n return resource;\n};\n\nexport const mapAdditionalFields = async (\n request: Request<unknown>,\n resource: MaybePost | MaybeAnswer | MaybeTag | MaybeCollection,\n routeOpts: RouteOptions,\n options?: {\n checkRights?: boolean;\n creds?: BackstageCredentials;\n username?: string;\n },\n) => {\n if (!resource) {\n return resource;\n }\n\n const { creds, username, checkRights = true } = options ?? {};\n const { permissionMgr } = routeOpts;\n const credentials =\n creds ?? (await permissionMgr.getCredentials(request, true));\n\n if (isTag(resource)) {\n return mapTagAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n checkRights,\n );\n } else if (isCollection(resource)) {\n return mapCollectionAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n checkRights,\n );\n }\n\n const user =\n username ??\n (await routeOpts.permissionMgr.getUsername(request, true, credentials));\n\n if (isPost(resource)) {\n return mapPostAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n user,\n checkRights,\n );\n } else if (isAnswer(resource)) {\n return mapAnswerAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n user,\n checkRights,\n );\n }\n\n return resource;\n};\n\nexport const stringDateTime = (dayString: string) => {\n const dateTimePeriod = Number(dayString.toString().slice(0, -1));\n return format(subDays(new Date(), dateTimePeriod), 'yyyy-MM-dd');\n};\n\nexport const getS3Client = (config: Config) => {\n const accessKeyId = config.getOptionalString('qeta.storage.accessKeyId');\n const secretAccessKey = config.getOptionalString(\n 'qeta.storage.secretAccessKey',\n );\n const region = config.getOptionalString('qeta.storage.region');\n const sessionToken = config.getOptionalString('qeta.storage.sessionToken');\n const endpoint = config.getOptionalString('qeta.storage.endpoint');\n const httpsProxy = config.getOptionalString('qeta.storage.httpsProxy');\n const forcePathStyle = config.getOptionalBoolean(\n 'qeta.storage.forcePathStyle',\n );\n const maxAttempts = config.getOptionalNumber('qeta.storage.maxAttempts');\n\n let credentials;\n if (accessKeyId && secretAccessKey) {\n credentials = {\n accessKeyId,\n secretAccessKey,\n sessionToken,\n };\n }\n return new S3Client({\n customUserAgent: 'backstage-aws-drodil-qeta-s3-storage',\n ...(credentials && { credentials }),\n ...(region && { region }),\n ...(endpoint && { endpoint }),\n ...(forcePathStyle && { forcePathStyle }),\n ...(maxAttempts && { maxAttempts }),\n ...(httpsProxy && {\n requestHandler: new NodeHttpHandler({\n httpsAgent: new HttpsProxyAgent({ proxy: httpsProxy }),\n }),\n }),\n });\n};\n\nexport const getAzureBlobServiceClient = (config: Config) => {\n const accountName = config.getOptionalString(\n 'qeta.storage.blobStorageAccountName',\n );\n const connectionString = config.getOptionalString(\n 'qeta.storage.blobStorageConnectionString',\n );\n if (connectionString) {\n return BlobServiceClient.fromConnectionString(connectionString);\n } else if (accountName) {\n return new BlobServiceClient(\n `https://${accountName}.blob.core.windows.net`,\n new DefaultAzureCredential(),\n );\n }\n\n throw new Error(\n 'Either account name or connection string must be provided for Azure Blob Storage',\n );\n};\n\nexport class ExpiryMap<K, V> extends Map<K, V> {\n #ttlMs: number;\n #timestamps: Map<K, number> = new Map();\n\n constructor(ttlMs: number) {\n super();\n this.#ttlMs = ttlMs;\n }\n\n set(key: K, value: V) {\n const result = super.set(key, value);\n this.#timestamps.set(key, new Date().getTime());\n this.clearOld();\n return result;\n }\n\n get(key: K) {\n this.clearOld();\n if (!this.has(key)) {\n return undefined;\n }\n return super.get(key);\n }\n\n delete(key: K) {\n this.#timestamps.delete(key);\n return super.delete(key);\n }\n\n clear() {\n this.#timestamps.clear();\n return super.clear();\n }\n\n clearOld() {\n const now = new Date().getTime();\n this.#timestamps.forEach((val, key) => {\n if (now - val > this.#ttlMs) {\n this.#timestamps.delete(key);\n super.delete(key);\n }\n });\n }\n}\n"],"names":["createConditionTransformer","rules","qetaEditTagPermission","qetaDeleteTagPermission","qetaEditCollectionPermission","qetaDeleteCollectionPermission","qetaEditCommentPermission","qetaDeleteCommentPermission","compact","qetaEditAnswerPermission","qetaDeleteAnswerPermission","qetaEditPostPermission","qetaDeletePostPermission","isTag","isCollection","isPost","isAnswer","format","subDays","S3Client","NodeHttpHandler","HttpsProxyAgent","BlobServiceClient","DefaultAzureCredential"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"util.cjs.js","sources":["../../src/service/util.ts"],"sourcesContent":["import { Request } from 'express';\nimport { format, subDays } from 'date-fns';\nimport {\n isAnswer,\n isCollection,\n isPost,\n isTag,\n MaybeAnswer,\n MaybeCollection,\n MaybePost,\n MaybeTag,\n} from '../database/QetaStore';\nimport { Config } from '@backstage/config';\nimport { S3Client } from '@aws-sdk/client-s3';\nimport { RouteOptions } from './types';\nimport {\n Answer,\n AnswerResponse,\n CollectionResponse,\n Comment,\n PostResponse,\n qetaDeleteAnswerPermission,\n qetaDeleteCollectionPermission,\n qetaDeleteCommentPermission,\n qetaDeletePostPermission,\n qetaDeleteTagPermission,\n qetaEditAnswerPermission,\n qetaEditCollectionPermission,\n qetaEditCommentPermission,\n qetaEditPostPermission,\n qetaEditTagPermission,\n TagResponse,\n} from '@drodil/backstage-plugin-qeta-common';\nimport { NodeHttpHandler } from '@smithy/node-http-handler';\nimport { HttpsProxyAgent } from 'hpagent';\nimport { compact } from 'lodash';\nimport {\n ConditionTransformer,\n createConditionTransformer,\n} from '@backstage/plugin-permission-node';\nimport { rules } from '@drodil/backstage-plugin-qeta-node';\nimport { BlobServiceClient } from '@azure/storage-blob';\nimport { DefaultAzureCredential } from '@azure/identity';\nimport { BackstageCredentials } from '@backstage/backend-plugin-api';\nimport { PermissionManager } from './PermissionManager.ts';\n\nexport const getCreated = async (\n req: Request<unknown>,\n options: RouteOptions,\n): Promise<Date> => {\n const allowMetadataInput = options.config.getOptionalBoolean(\n 'qeta.allowMetadataInput',\n );\n\n if (allowMetadataInput && req.body.created) {\n return new Date(req.body.created);\n }\n\n return new Date();\n};\n\nexport type QetaFilter = {\n property:\n | 'posts.id'\n | 'posts.author'\n | 'posts.type'\n | 'tags'\n | 'entityRefs'\n | 'answers.id'\n | 'answers.author'\n | 'comments.id'\n | 'comments.author'\n | 'tags.tag'\n | 'tag.experts'\n | 'collections.owner'\n | 'collections.id';\n values: Array<string | undefined>;\n};\n\nexport type QetaFilters =\n | { anyOf: QetaFilter[] }\n | { allOf: QetaFilter[] }\n | { not: QetaFilter }\n | QetaFilter;\n\nexport const transformConditions: ConditionTransformer<QetaFilters> =\n createConditionTransformer(Object.values(rules));\n\nconst mapTagAdditionalFields = async (\n request: Request<unknown>,\n resource: TagResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n checkRights?: boolean,\n) => {\n const [canEdit, canDelete] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditTagPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaDeleteTagPermission, {\n resource,\n credentials,\n })\n : undefined,\n ]);\n\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n return resource;\n};\n\nconst mapCollectionAdditionalFields = async (\n request: Request<unknown>,\n resource: CollectionResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n checkRights?: boolean,\n) => {\n const [canEdit, canDelete] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditCollectionPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(\n request,\n qetaDeleteCollectionPermission,\n {\n resource,\n credentials,\n },\n )\n : undefined,\n ]);\n\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n return resource;\n};\n\nconst mapResourceComments = async (\n request: Request<unknown>,\n resource: AnswerResponse | PostResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n const commentArr = resource.comments ?? [];\n const editPermissions = await Promise.all(\n commentArr.map(async (c: Comment) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(\n request,\n qetaEditCommentPermission,\n { resource: c, credentials },\n );\n }),\n );\n\n const deletePermissions = await Promise.all(\n commentArr.map(async (c: Comment) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(\n request,\n qetaDeleteCommentPermission,\n { resource: c, credentials },\n );\n }),\n );\n\n const comments: (Comment | null)[] = commentArr.map(\n (c: Comment, index): Comment => {\n return {\n ...c,\n own: c.author === username,\n expert: c.experts?.includes(c.author),\n canEdit: editPermissions[index],\n canDelete: deletePermissions[index],\n };\n },\n );\n return compact(comments);\n};\n\nconst mapAnswerAdditionalFields = async (\n request: Request<unknown>,\n resource: AnswerResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n const [canEdit, canDelete, comments] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditAnswerPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaDeleteAnswerPermission, {\n resource,\n credentials,\n })\n : undefined,\n mapResourceComments(\n request,\n resource,\n permissionMgr,\n credentials,\n username,\n checkRights,\n ),\n ]);\n\n resource.ownVote = resource.votes?.find(v => v.author === username)?.score;\n resource.own = resource.author === username;\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n resource.expert = resource.experts?.includes(resource.author);\n resource.comments = comments;\n\n return resource;\n};\n\nconst mapPostAnswers = async (\n request: Request<unknown>,\n resource: PostResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n const answersArray = resource.answers ?? [];\n const editPermissions = await Promise.all(\n answersArray.map(async (a: Answer) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(request, qetaEditAnswerPermission, {\n resource: a,\n credentials,\n });\n }),\n );\n\n const deletePermissions = await Promise.all(\n answersArray.map(async (a: Answer) => {\n if (!checkRights) {\n return undefined;\n }\n return permissionMgr.authorizeBoolean(\n request,\n qetaDeleteAnswerPermission,\n { resource: a, credentials },\n );\n }),\n );\n\n const comments = await Promise.all(\n answersArray.map(async (a: Answer) => {\n return mapResourceComments(\n request,\n a,\n permissionMgr,\n credentials,\n username,\n checkRights,\n );\n }),\n );\n\n return answersArray.map((a: Answer, index: number) => {\n return {\n ...a,\n ownVote: a.votes?.find(v => v.author === username)?.score,\n own: resource.author === username,\n canEdit: editPermissions[index],\n canDelete: deletePermissions[index],\n expert: a.experts?.includes(resource.author),\n comments: comments[index],\n };\n });\n};\n\nconst mapPostAdditionalFields = async (\n request: Request<unknown>,\n resource: PostResponse,\n permissionMgr: PermissionManager,\n credentials: BackstageCredentials,\n username: string,\n checkRights?: boolean,\n) => {\n resource.ownVote = resource.votes?.find(v => v.author === username)?.score;\n resource.own = resource.author === username;\n\n const [canEdit, canDelete, answers, comments] = await Promise.all([\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaEditPostPermission, {\n resource,\n credentials,\n })\n : undefined,\n checkRights\n ? permissionMgr.authorizeBoolean(request, qetaDeletePostPermission, {\n resource,\n credentials,\n })\n : undefined,\n mapPostAnswers(\n request,\n resource,\n permissionMgr,\n credentials,\n username,\n checkRights,\n ),\n mapResourceComments(\n request,\n resource,\n permissionMgr,\n credentials,\n username,\n checkRights,\n ),\n ]);\n resource.canEdit = canEdit;\n resource.canDelete = canDelete;\n resource.answers = answers;\n resource.comments = comments;\n return resource;\n};\n\nexport const mapAdditionalFields = async (\n request: Request<unknown>,\n resource: MaybePost | MaybeAnswer | MaybeTag | MaybeCollection,\n routeOpts: RouteOptions,\n options?: {\n checkRights?: boolean;\n creds?: BackstageCredentials;\n username?: string;\n },\n) => {\n if (!resource) {\n return resource;\n }\n\n const { creds, username, checkRights = true } = options ?? {};\n const { permissionMgr } = routeOpts;\n const credentials =\n creds ?? (await permissionMgr.getCredentials(request, true));\n\n if (isTag(resource)) {\n return mapTagAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n checkRights,\n );\n } else if (isCollection(resource)) {\n return mapCollectionAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n checkRights,\n );\n }\n\n const user =\n username ??\n (await routeOpts.permissionMgr.getUsername(request, true, credentials));\n\n if (isPost(resource)) {\n return mapPostAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n user,\n checkRights,\n );\n } else if (isAnswer(resource)) {\n return mapAnswerAdditionalFields(\n request,\n resource,\n permissionMgr,\n credentials,\n user,\n checkRights,\n );\n }\n\n return resource;\n};\n\nexport const stringDateTime = (dayString: string) => {\n const dateTimePeriod = Number(dayString.toString().slice(0, -1));\n return format(subDays(new Date(), dateTimePeriod), 'yyyy-MM-dd');\n};\n\nexport const getS3Client = (config: Config) => {\n const accessKeyId = config.getOptionalString('qeta.storage.accessKeyId');\n const secretAccessKey = config.getOptionalString(\n 'qeta.storage.secretAccessKey',\n );\n const region = config.getOptionalString('qeta.storage.region');\n const sessionToken = config.getOptionalString('qeta.storage.sessionToken');\n const endpoint = config.getOptionalString('qeta.storage.endpoint');\n const httpsProxy = config.getOptionalString('qeta.storage.httpsProxy');\n const forcePathStyle = config.getOptionalBoolean(\n 'qeta.storage.forcePathStyle',\n );\n const maxAttempts = config.getOptionalNumber('qeta.storage.maxAttempts');\n\n let credentials;\n if (accessKeyId && secretAccessKey) {\n credentials = {\n accessKeyId,\n secretAccessKey,\n sessionToken,\n };\n }\n return new S3Client({\n customUserAgent: 'backstage-aws-drodil-qeta-s3-storage',\n ...(credentials && { credentials }),\n ...(region && { region }),\n ...(endpoint && { endpoint }),\n ...(forcePathStyle && { forcePathStyle }),\n ...(maxAttempts && { maxAttempts }),\n ...(httpsProxy && {\n requestHandler: new NodeHttpHandler({\n httpsAgent: new HttpsProxyAgent({ proxy: httpsProxy }),\n }),\n }),\n });\n};\n\nexport const getAzureBlobServiceClient = (config: Config) => {\n const accountName = config.getOptionalString(\n 'qeta.storage.blobStorageAccountName',\n );\n const connectionString = config.getOptionalString(\n 'qeta.storage.blobStorageConnectionString',\n );\n if (connectionString) {\n return BlobServiceClient.fromConnectionString(connectionString);\n } else if (accountName) {\n return new BlobServiceClient(\n `https://${accountName}.blob.core.windows.net`,\n new DefaultAzureCredential(),\n );\n }\n\n throw new Error(\n 'Either account name or connection string must be provided for Azure Blob Storage',\n );\n};\n"],"names":["createConditionTransformer","rules","qetaEditTagPermission","qetaDeleteTagPermission","qetaEditCollectionPermission","qetaDeleteCollectionPermission","qetaEditCommentPermission","qetaDeleteCommentPermission","compact","qetaEditAnswerPermission","qetaDeleteAnswerPermission","qetaEditPostPermission","qetaDeletePostPermission","isTag","isCollection","isPost","isAnswer","format","subDays","S3Client","NodeHttpHandler","HttpsProxyAgent","BlobServiceClient","DefaultAzureCredential"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drodil/backstage-plugin-qeta-backend",
|
|
3
|
-
"version": "3.45.
|
|
3
|
+
"version": "3.45.2",
|
|
4
4
|
"description": "Backstage.io Q&A plugin backend",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"backstage",
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
"@backstage/plugin-permission-node": "^0.10.6",
|
|
67
67
|
"@backstage/plugin-signals-node": "^0.1.26",
|
|
68
68
|
"@backstage/types": "^1.2.2",
|
|
69
|
-
"@drodil/backstage-plugin-qeta-common": "^3.45.
|
|
70
|
-
"@drodil/backstage-plugin-qeta-node": "^3.45.
|
|
69
|
+
"@drodil/backstage-plugin-qeta-common": "^3.45.2",
|
|
70
|
+
"@drodil/backstage-plugin-qeta-node": "^3.45.2",
|
|
71
71
|
"@smithy/node-http-handler": "^4.0.2",
|
|
72
72
|
"@types/express": "*",
|
|
73
73
|
"@types/mime-types": "^2.1.4",
|
|
@@ -105,9 +105,9 @@
|
|
|
105
105
|
"@backstage/plugin-search-backend-module-pg": "^0.5.50",
|
|
106
106
|
"@backstage/plugin-signals-backend": "^0.3.10",
|
|
107
107
|
"@backstage/plugin-techdocs-backend": "^2.1.2",
|
|
108
|
-
"@drodil/backstage-plugin-catalog-backend-module-qeta": "^3.45.
|
|
109
|
-
"@drodil/backstage-plugin-qeta-backend-module-openai": "^3.45.
|
|
110
|
-
"@drodil/backstage-plugin-search-backend-module-qeta": "^3.45.
|
|
108
|
+
"@drodil/backstage-plugin-catalog-backend-module-qeta": "^3.45.2",
|
|
109
|
+
"@drodil/backstage-plugin-qeta-backend-module-openai": "^3.45.2",
|
|
110
|
+
"@drodil/backstage-plugin-search-backend-module-qeta": "^3.45.2",
|
|
111
111
|
"@types/sanitize-html": "^2.9.5",
|
|
112
112
|
"@types/stopword": "^2.0.3",
|
|
113
113
|
"@types/supertest": "^2.0.12",
|