@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 loader = this.getAuthorizeLoader(credentials);
94
- const result = await loader.load({
95
- permission: backstagePluginQetaCommon.qetaModeratePermission
96
- });
97
- return result.result === pluginPermissionCommon.AuthorizeResult.ALLOW;
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
- decision = await loader.load({ permission, resourceRef });
202
+ const result = await this.permissions.authorize(
203
+ [{ permission, resourceRef }],
204
+ { credentials }
205
+ );
206
+ decision = result[0];
214
207
  } else {
215
- decision = await loader.load({ permission });
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 loader = this.getAuthorizeConditionalLoader(credentials);
261
- decision = await loader.load({ permission });
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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"}
@@ -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.1",
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.1",
70
- "@drodil/backstage-plugin-qeta-node": "^3.45.1",
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.1",
109
- "@drodil/backstage-plugin-qeta-backend-module-openai": "^3.45.1",
110
- "@drodil/backstage-plugin-search-backend-module-qeta": "^3.45.1",
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",