@backstage/plugin-permission-common 0.9.0-next.0 → 0.9.1-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @backstage/plugin-permission-common
|
|
2
2
|
|
|
3
|
+
## 0.9.1-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3507fcd: Just some more circular dep cleanup
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/config@1.3.3-next.0
|
|
10
|
+
|
|
11
|
+
## 0.9.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 4da2965: Fixed an issue causing the `PermissionClient` to exhaust the request body size limit too quickly when making many requests.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 37328b1: Fixed an issue causing `PermissionClient` to throw an error when authorizing basic permissions with the `permission.EXPERIMENTAL_enableBatchedRequests` config enabled.
|
|
20
|
+
- 72d019d: Removed various typos
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
- @backstage/config@1.3.2
|
|
23
|
+
- @backstage/errors@1.2.7
|
|
24
|
+
- @backstage/types@1.2.1
|
|
25
|
+
|
|
3
26
|
## 0.9.0-next.0
|
|
4
27
|
|
|
5
28
|
### Minor Changes
|
|
@@ -47,7 +47,7 @@ const authorizePermissionResponseBatchSchema = zod.z.object({
|
|
|
47
47
|
zod.z.literal(api.AuthorizeResult.DENY)
|
|
48
48
|
])
|
|
49
49
|
)
|
|
50
|
-
});
|
|
50
|
+
}).or(authorizePermissionResponseSchema);
|
|
51
51
|
const queryPermissionResponseSchema = zod.z.union([
|
|
52
52
|
zod.z.object({
|
|
53
53
|
result: zod.z.literal(api.AuthorizeResult.ALLOW).or(zod.z.literal(api.AuthorizeResult.DENY))
|
|
@@ -160,9 +160,12 @@ class PermissionClient {
|
|
|
160
160
|
return queries.map((query) => {
|
|
161
161
|
const { id } = request[query.permission.name];
|
|
162
162
|
const item = responsesById[id];
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
if (Array.isArray(item.result)) {
|
|
164
|
+
return {
|
|
165
|
+
result: query.resourceRef ? item.result.shift() : item.result[0]
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return { result: item.result };
|
|
166
169
|
});
|
|
167
170
|
}
|
|
168
171
|
async makeRawRequest(request, itemSchema, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionClient.cjs.js","sources":["../src/PermissionClient.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n PermissionMessageBatch,\n PermissionCriteria,\n PermissionCondition,\n PermissionEvaluator,\n QueryPermissionRequest,\n AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n QueryPermissionResponse,\n IdentifiedPermissionMessage,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport {\n AuthorizeRequestOptions,\n BasicPermission,\n ResourcePermission,\n} from './types/permission';\nimport { isResourcePermission } from './permissions';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.record(z.any()).optional(),\n })\n .or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ not: permissionCriteriaSchema })),\n);\n\nconst authorizePermissionResponseSchema: z.ZodSchema<AuthorizePermissionResponse> =\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n });\n\nconst authorizePermissionResponseBatchSchema = z.object({\n result: z.array(\n z.union([\n z.literal(AuthorizeResult.ALLOW),\n z.literal(AuthorizeResult.DENY),\n ]),\n ),\n});\n\nconst queryPermissionResponseSchema: z.ZodSchema<QueryPermissionResponse> =\n z.union([\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n z.object({\n result: z.literal(AuthorizeResult.CONDITIONAL),\n pluginId: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n }),\n ]);\n\nconst responseSchema = <T>(\n itemSchema: z.ZodSchema<T>,\n ids: Set<string>,\n): z.ZodSchema<PermissionMessageBatch<T>> =>\n z.object({\n items: z\n .array(\n z.intersection(\n z.object({\n id: z.string(),\n }),\n itemSchema,\n ),\n )\n .refine(\n items =>\n items.length === ids.size && items.every(({ id }) => ids.has(id)),\n {\n message: 'Items in response do not match request',\n },\n ),\n });\n\n/**\n * Options for {@link PermissionClient} requests.\n *\n * @public\n */\nexport type PermissionClientRequestOptions = {\n token?: string;\n};\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionEvaluator {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n private readonly enableBatchedRequests: boolean;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n\n this.enableBatchedRequests =\n options.config.getOptionalBoolean(\n 'permission.EXPERIMENTAL_enableBatchedRequests',\n ) ?? false;\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorize}\n */\n async authorize(\n requests: AuthorizePermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> {\n if (!this.enabled) {\n return requests.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n if (this.enableBatchedRequests) {\n return this.makeBatchedRequest(requests, options);\n }\n\n return this.makeRequest(\n requests,\n authorizePermissionResponseSchema,\n options,\n );\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorizeConditional}\n */\n async authorizeConditional(\n queries: QueryPermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n return this.makeRequest(queries, queryPermissionResponseSchema, options);\n }\n\n private async makeRequest<TQuery, TResult>(\n queries: TQuery[],\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const request: PermissionMessageBatch<TQuery> = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const parsedResponse = await this.makeRawRequest(\n request,\n itemSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, z.infer<typeof itemSchema>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private async makeBatchedRequest(\n queries: AuthorizePermissionRequest[],\n options?: AuthorizeRequestOptions,\n ) {\n const request: Record<string, BatchedAuthorizePermissionRequest> = {};\n\n for (const query of queries) {\n const { permission, resourceRef } = query;\n\n if (isResourcePermission(permission)) {\n request[permission.name] ||= {\n permission,\n resourceRef: [],\n id: uuid.v4(),\n };\n\n if (resourceRef) {\n request[permission.name].resourceRef?.push(resourceRef);\n }\n } else {\n request[permission.name] ||= {\n permission,\n id: uuid.v4(),\n };\n }\n }\n\n const parsedResponse = await this.makeRawRequest(\n { items: Object.values(request) },\n authorizePermissionResponseBatchSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, (typeof parsedResponse)['items'][number]>);\n\n return queries.map(query => {\n const { id } = request[query.permission.name];\n\n const item = responsesById[id];\n return {\n result: query.resourceRef ? item.result.shift()! : item.result[0],\n };\n });\n }\n\n private async makeRawRequest<TQuery, TResult>(\n request: PermissionMessageBatch<TQuery>,\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n\n return responseSchema(\n itemSchema,\n new Set(request.items.map(({ id }) => id)),\n ).parse(responseBody);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n}\n\n/**\n * @internal\n */\nexport type BatchedAuthorizePermissionRequest = IdentifiedPermissionMessage<\n | {\n permission: BasicPermission;\n resourceRef?: undefined;\n }\n | { permission: ResourcePermission; resourceRef: string[] }\n>;\n"],"names":["z","AuthorizeResult","uuid","isResourcePermission","fetch","ResponseError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PermissionClient.cjs.js","sources":["../src/PermissionClient.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n PermissionMessageBatch,\n PermissionCriteria,\n PermissionCondition,\n PermissionEvaluator,\n QueryPermissionRequest,\n AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n QueryPermissionResponse,\n IdentifiedPermissionMessage,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport {\n AuthorizeRequestOptions,\n BasicPermission,\n ResourcePermission,\n} from './types/permission';\nimport { isResourcePermission } from './permissions';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.record(z.any()).optional(),\n })\n .or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ not: permissionCriteriaSchema })),\n);\n\nconst authorizePermissionResponseSchema: z.ZodSchema<AuthorizePermissionResponse> =\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n });\n\nconst authorizePermissionResponseBatchSchema = z\n .object({\n result: z.array(\n z.union([\n z.literal(AuthorizeResult.ALLOW),\n z.literal(AuthorizeResult.DENY),\n ]),\n ),\n })\n .or(authorizePermissionResponseSchema);\n\nconst queryPermissionResponseSchema: z.ZodSchema<QueryPermissionResponse> =\n z.union([\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n z.object({\n result: z.literal(AuthorizeResult.CONDITIONAL),\n pluginId: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n }),\n ]);\n\nconst responseSchema = <T>(\n itemSchema: z.ZodSchema<T>,\n ids: Set<string>,\n): z.ZodSchema<PermissionMessageBatch<T>> =>\n z.object({\n items: z\n .array(\n z.intersection(\n z.object({\n id: z.string(),\n }),\n itemSchema,\n ),\n )\n .refine(\n items =>\n items.length === ids.size && items.every(({ id }) => ids.has(id)),\n {\n message: 'Items in response do not match request',\n },\n ),\n });\n\n/**\n * Options for {@link PermissionClient} requests.\n *\n * @public\n */\nexport type PermissionClientRequestOptions = {\n token?: string;\n};\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionEvaluator {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n private readonly enableBatchedRequests: boolean;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n\n this.enableBatchedRequests =\n options.config.getOptionalBoolean(\n 'permission.EXPERIMENTAL_enableBatchedRequests',\n ) ?? false;\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorize}\n */\n async authorize(\n requests: AuthorizePermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> {\n if (!this.enabled) {\n return requests.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n if (this.enableBatchedRequests) {\n return this.makeBatchedRequest(requests, options);\n }\n\n return this.makeRequest(\n requests,\n authorizePermissionResponseSchema,\n options,\n );\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorizeConditional}\n */\n async authorizeConditional(\n queries: QueryPermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n return this.makeRequest(queries, queryPermissionResponseSchema, options);\n }\n\n private async makeRequest<TQuery, TResult>(\n queries: TQuery[],\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const request: PermissionMessageBatch<TQuery> = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const parsedResponse = await this.makeRawRequest(\n request,\n itemSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, z.infer<typeof itemSchema>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private async makeBatchedRequest(\n queries: AuthorizePermissionRequest[],\n options?: AuthorizeRequestOptions,\n ) {\n const request: Record<string, BatchedAuthorizePermissionRequest> = {};\n\n for (const query of queries) {\n const { permission, resourceRef } = query;\n\n if (isResourcePermission(permission)) {\n request[permission.name] ||= {\n permission,\n resourceRef: [],\n id: uuid.v4(),\n };\n\n if (resourceRef) {\n request[permission.name].resourceRef?.push(resourceRef);\n }\n } else {\n request[permission.name] ||= {\n permission,\n id: uuid.v4(),\n };\n }\n }\n\n const parsedResponse = await this.makeRawRequest(\n { items: Object.values(request) },\n authorizePermissionResponseBatchSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, (typeof parsedResponse)['items'][number]>);\n\n return queries.map(query => {\n const { id } = request[query.permission.name];\n\n const item = responsesById[id];\n\n if (Array.isArray(item.result)) {\n return {\n result: query.resourceRef ? item.result.shift()! : item.result[0],\n };\n }\n return { result: item.result };\n });\n }\n\n private async makeRawRequest<TQuery, TResult>(\n request: PermissionMessageBatch<TQuery>,\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n\n return responseSchema(\n itemSchema,\n new Set(request.items.map(({ id }) => id)),\n ).parse(responseBody);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n}\n\n/**\n * @internal\n */\nexport type BatchedAuthorizePermissionRequest = IdentifiedPermissionMessage<\n | {\n permission: BasicPermission;\n resourceRef?: undefined;\n }\n | { permission: ResourcePermission; resourceRef: string[] }\n>;\n"],"names":["z","AuthorizeResult","uuid","isResourcePermission","fetch","ResponseError"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAM,2BAEFA,KAAE,CAAA,IAAA;AAAA,EAAK,MACTA,MACG,MAAO,CAAA;AAAA,IACN,IAAA,EAAMA,MAAE,MAAO,EAAA;AAAA,IACf,YAAA,EAAcA,MAAE,MAAO,EAAA;AAAA,IACvB,QAAQA,KAAE,CAAA,MAAA,CAAOA,MAAE,GAAI,EAAC,EAAE,QAAS;AAAA,GACpC,CACA,CAAA,EAAA,CAAGA,KAAE,CAAA,MAAA,CAAO,EAAE,KAAO,EAAAA,KAAA,CAAE,KAAM,CAAA,wBAAwB,EAAE,QAAS,EAAA,EAAG,CAAC,EACpE,EAAG,CAAAA,KAAA,CAAE,MAAO,CAAA,EAAE,OAAOA,KAAE,CAAA,KAAA,CAAM,wBAAwB,CAAA,CAAE,UAAW,EAAC,CAAC,CAAA,CACpE,GAAGA,KAAE,CAAA,MAAA,CAAO,EAAE,GAAK,EAAA,wBAAA,EAA0B,CAAC;AACnD,CAAA;AAEA,MAAM,iCAAA,GACJA,MAAE,MAAO,CAAA;AAAA,EACP,MAAA,EAAQA,KACL,CAAA,OAAA,CAAQC,mBAAgB,CAAA,KAAK,CAC7B,CAAA,EAAA,CAAGD,KAAE,CAAA,OAAA,CAAQC,mBAAgB,CAAA,IAAI,CAAC;AACvC,CAAC,CAAA;AAEH,MAAM,sCAAA,GAAyCD,MAC5C,MAAO,CAAA;AAAA,EACN,QAAQA,KAAE,CAAA,KAAA;AAAA,IACRA,MAAE,KAAM,CAAA;AAAA,MACNA,KAAA,CAAE,OAAQ,CAAAC,mBAAA,CAAgB,KAAK,CAAA;AAAA,MAC/BD,KAAA,CAAE,OAAQ,CAAAC,mBAAA,CAAgB,IAAI;AAAA,KAC/B;AAAA;AAEL,CAAC,CAAA,CACA,GAAG,iCAAiC,CAAA;AAEvC,MAAM,6BAAA,GACJD,MAAE,KAAM,CAAA;AAAA,EACNA,MAAE,MAAO,CAAA;AAAA,IACP,MAAA,EAAQA,KACL,CAAA,OAAA,CAAQC,mBAAgB,CAAA,KAAK,CAC7B,CAAA,EAAA,CAAGD,KAAE,CAAA,OAAA,CAAQC,mBAAgB,CAAA,IAAI,CAAC;AAAA,GACtC,CAAA;AAAA,EACDD,MAAE,MAAO,CAAA;AAAA,IACP,MAAQ,EAAAA,KAAA,CAAE,OAAQ,CAAAC,mBAAA,CAAgB,WAAW,CAAA;AAAA,IAC7C,QAAA,EAAUD,MAAE,MAAO,EAAA;AAAA,IACnB,YAAA,EAAcA,MAAE,MAAO,EAAA;AAAA,IACvB,UAAY,EAAA;AAAA,GACb;AACH,CAAC,CAAA;AAEH,MAAM,cAAiB,GAAA,CACrB,UACA,EAAA,GAAA,KAEAA,MAAE,MAAO,CAAA;AAAA,EACP,OAAOA,KACJ,CAAA,KAAA;AAAA,IACCA,KAAE,CAAA,YAAA;AAAA,MACAA,MAAE,MAAO,CAAA;AAAA,QACP,EAAA,EAAIA,MAAE,MAAO;AAAA,OACd,CAAA;AAAA,MACD;AAAA;AACF,GAED,CAAA,MAAA;AAAA,IACC,CACE,KAAA,KAAA,KAAA,CAAM,MAAW,KAAA,GAAA,CAAI,QAAQ,KAAM,CAAA,KAAA,CAAM,CAAC,EAAE,EAAG,EAAA,KAAM,GAAI,CAAA,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,IAClE;AAAA,MACE,OAAS,EAAA;AAAA;AACX;AAEN,CAAC,CAAA;AAeI,MAAM,gBAAgD,CAAA;AAAA,EAC1C,OAAA;AAAA,EACA,SAAA;AAAA,EACA,qBAAA;AAAA,EAEjB,YAAY,OAAsD,EAAA;AAChE,IAAA,IAAA,CAAK,YAAY,OAAQ,CAAA,SAAA;AACzB,IAAA,IAAA,CAAK,OACH,GAAA,OAAA,CAAQ,MAAO,CAAA,kBAAA,CAAmB,oBAAoB,CAAK,IAAA,KAAA;AAE7D,IAAK,IAAA,CAAA,qBAAA,GACH,QAAQ,MAAO,CAAA,kBAAA;AAAA,MACb;AAAA,KACG,IAAA,KAAA;AAAA;AACT;AAAA;AAAA;AAAA,EAKA,MAAM,SACJ,CAAA,QAAA,EACA,OACwC,EAAA;AACxC,IAAI,IAAA,CAAC,KAAK,OAAS,EAAA;AACjB,MAAA,OAAO,SAAS,GAAI,CAAA,CAAA,CAAA,MAAM,EAAE,MAAQ,EAAAC,mBAAA,CAAgB,OAAiB,CAAA,CAAA;AAAA;AAGvE,IAAA,IAAI,KAAK,qBAAuB,EAAA;AAC9B,MAAO,OAAA,IAAA,CAAK,kBAAmB,CAAA,QAAA,EAAU,OAAO,CAAA;AAAA;AAGlD,IAAA,OAAO,IAAK,CAAA,WAAA;AAAA,MACV,QAAA;AAAA,MACA,iCAAA;AAAA,MACA;AAAA,KACF;AAAA;AACF;AAAA;AAAA;AAAA,EAKA,MAAM,oBACJ,CAAA,OAAA,EACA,OACoC,EAAA;AACpC,IAAI,IAAA,CAAC,KAAK,OAAS,EAAA;AACjB,MAAA,OAAO,QAAQ,GAAI,CAAA,CAAA,CAAA,MAAM,EAAE,MAAQ,EAAAA,mBAAA,CAAgB,OAAiB,CAAA,CAAA;AAAA;AAGtE,IAAA,OAAO,IAAK,CAAA,WAAA,CAAY,OAAS,EAAA,6BAAA,EAA+B,OAAO,CAAA;AAAA;AACzE,EAEA,MAAc,WAAA,CACZ,OACA,EAAA,UAAA,EACA,OACA,EAAA;AACA,IAAA,MAAM,OAA0C,GAAA;AAAA,MAC9C,KAAA,EAAO,OAAQ,CAAA,GAAA,CAAI,CAAU,KAAA,MAAA;AAAA,QAC3B,EAAA,EAAIC,gBAAK,EAAG,EAAA;AAAA,QACZ,GAAG;AAAA,OACH,CAAA;AAAA,KACJ;AAEA,IAAM,MAAA,cAAA,GAAiB,MAAM,IAAK,CAAA,cAAA;AAAA,MAChC,OAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,gBAAgB,cAAe,CAAA,KAAA,CAAM,MAAO,CAAA,CAAC,KAAK,CAAM,KAAA;AAC5D,MAAI,GAAA,CAAA,CAAA,CAAE,EAAE,CAAI,GAAA,CAAA;AACZ,MAAO,OAAA,GAAA;AAAA,KACT,EAAG,EAAgD,CAAA;AAEnD,IAAA,OAAO,QAAQ,KAAM,CAAA,GAAA,CAAI,WAAS,aAAc,CAAA,KAAA,CAAM,EAAE,CAAC,CAAA;AAAA;AAC3D,EAEA,MAAc,kBACZ,CAAA,OAAA,EACA,OACA,EAAA;AACA,IAAA,MAAM,UAA6D,EAAC;AAEpE,IAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,MAAM,MAAA,EAAE,UAAY,EAAA,WAAA,EAAgB,GAAA,KAAA;AAEpC,MAAI,IAAAC,yBAAA,CAAqB,UAAU,CAAG,EAAA;AACpC,QAAQ,OAAA,CAAA,UAAA,CAAW,IAAI,CAAM,KAAA;AAAA,UAC3B,UAAA;AAAA,UACA,aAAa,EAAC;AAAA,UACd,EAAA,EAAID,gBAAK,EAAG;AAAA,SACd;AAEA,QAAA,IAAI,WAAa,EAAA;AACf,UAAA,OAAA,CAAQ,UAAW,CAAA,IAAI,CAAE,CAAA,WAAA,EAAa,KAAK,WAAW,CAAA;AAAA;AACxD,OACK,MAAA;AACL,QAAQ,OAAA,CAAA,UAAA,CAAW,IAAI,CAAM,KAAA;AAAA,UAC3B,UAAA;AAAA,UACA,EAAA,EAAIA,gBAAK,EAAG;AAAA,SACd;AAAA;AACF;AAGF,IAAM,MAAA,cAAA,GAAiB,MAAM,IAAK,CAAA,cAAA;AAAA,MAChC,EAAE,KAAA,EAAO,MAAO,CAAA,MAAA,CAAO,OAAO,CAAE,EAAA;AAAA,MAChC,sCAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,gBAAgB,cAAe,CAAA,KAAA,CAAM,MAAO,CAAA,CAAC,KAAK,CAAM,KAAA;AAC5D,MAAI,GAAA,CAAA,CAAA,CAAE,EAAE,CAAI,GAAA,CAAA;AACZ,MAAO,OAAA,GAAA;AAAA,KACT,EAAG,EAA8D,CAAA;AAEjE,IAAO,OAAA,OAAA,CAAQ,IAAI,CAAS,KAAA,KAAA;AAC1B,MAAA,MAAM,EAAE,EAAG,EAAA,GAAI,OAAQ,CAAA,KAAA,CAAM,WAAW,IAAI,CAAA;AAE5C,MAAM,MAAA,IAAA,GAAO,cAAc,EAAE,CAAA;AAE7B,MAAA,IAAI,KAAM,CAAA,OAAA,CAAQ,IAAK,CAAA,MAAM,CAAG,EAAA;AAC9B,QAAO,OAAA;AAAA,UACL,MAAA,EAAQ,MAAM,WAAc,GAAA,IAAA,CAAK,OAAO,KAAM,EAAA,GAAK,IAAK,CAAA,MAAA,CAAO,CAAC;AAAA,SAClE;AAAA;AAEF,MAAO,OAAA,EAAE,MAAQ,EAAA,IAAA,CAAK,MAAO,EAAA;AAAA,KAC9B,CAAA;AAAA;AACH,EAEA,MAAc,cAAA,CACZ,OACA,EAAA,UAAA,EACA,OACA,EAAA;AACA,IAAA,MAAM,aAAgB,GAAA,MAAM,IAAK,CAAA,SAAA,CAAU,WAAW,YAAY,CAAA;AAClE,IAAA,MAAM,QAAW,GAAA,MAAME,sBAAM,CAAA,CAAA,EAAG,aAAa,CAAc,UAAA,CAAA,EAAA;AAAA,MACzD,MAAQ,EAAA,MAAA;AAAA,MACR,IAAA,EAAM,IAAK,CAAA,SAAA,CAAU,OAAO,CAAA;AAAA,MAC5B,OAAS,EAAA;AAAA,QACP,GAAG,IAAA,CAAK,sBAAuB,CAAA,OAAA,EAAS,KAAK,CAAA;AAAA,QAC7C,cAAgB,EAAA;AAAA;AAClB,KACD,CAAA;AACD,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,MAAMC,oBAAc,CAAA,YAAA,CAAa,QAAQ,CAAA;AAAA;AAGjD,IAAM,MAAA,YAAA,GAAe,MAAM,QAAA,CAAS,IAAK,EAAA;AAEzC,IAAO,OAAA,cAAA;AAAA,MACL,UAAA;AAAA,MACA,IAAI,GAAI,CAAA,OAAA,CAAQ,KAAM,CAAA,GAAA,CAAI,CAAC,EAAE,EAAA,EAAS,KAAA,EAAE,CAAC;AAAA,KAC3C,CAAE,MAAM,YAAY,CAAA;AAAA;AACtB,EAEQ,uBAAuB,KAAwC,EAAA;AACrE,IAAA,OAAO,QAAQ,EAAE,aAAA,EAAe,UAAU,KAAK,CAAA,CAAA,KAAO,EAAC;AAAA;AAE3D;;;;"}
|
|
@@ -22,7 +22,7 @@ const authorizePermissionResponseBatchSchema = z.object({
|
|
|
22
22
|
z.literal(AuthorizeResult.DENY)
|
|
23
23
|
])
|
|
24
24
|
)
|
|
25
|
-
});
|
|
25
|
+
}).or(authorizePermissionResponseSchema);
|
|
26
26
|
const queryPermissionResponseSchema = z.union([
|
|
27
27
|
z.object({
|
|
28
28
|
result: z.literal(AuthorizeResult.ALLOW).or(z.literal(AuthorizeResult.DENY))
|
|
@@ -135,9 +135,12 @@ class PermissionClient {
|
|
|
135
135
|
return queries.map((query) => {
|
|
136
136
|
const { id } = request[query.permission.name];
|
|
137
137
|
const item = responsesById[id];
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
if (Array.isArray(item.result)) {
|
|
139
|
+
return {
|
|
140
|
+
result: query.resourceRef ? item.result.shift() : item.result[0]
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return { result: item.result };
|
|
141
144
|
});
|
|
142
145
|
}
|
|
143
146
|
async makeRawRequest(request, itemSchema, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionClient.esm.js","sources":["../src/PermissionClient.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n PermissionMessageBatch,\n PermissionCriteria,\n PermissionCondition,\n PermissionEvaluator,\n QueryPermissionRequest,\n AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n QueryPermissionResponse,\n IdentifiedPermissionMessage,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport {\n AuthorizeRequestOptions,\n BasicPermission,\n ResourcePermission,\n} from './types/permission';\nimport { isResourcePermission } from './permissions';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.record(z.any()).optional(),\n })\n .or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ not: permissionCriteriaSchema })),\n);\n\nconst authorizePermissionResponseSchema: z.ZodSchema<AuthorizePermissionResponse> =\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n });\n\nconst authorizePermissionResponseBatchSchema = z.object({\n result: z.array(\n z.union([\n z.literal(AuthorizeResult.ALLOW),\n z.literal(AuthorizeResult.DENY),\n ]),\n ),\n});\n\nconst queryPermissionResponseSchema: z.ZodSchema<QueryPermissionResponse> =\n z.union([\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n z.object({\n result: z.literal(AuthorizeResult.CONDITIONAL),\n pluginId: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n }),\n ]);\n\nconst responseSchema = <T>(\n itemSchema: z.ZodSchema<T>,\n ids: Set<string>,\n): z.ZodSchema<PermissionMessageBatch<T>> =>\n z.object({\n items: z\n .array(\n z.intersection(\n z.object({\n id: z.string(),\n }),\n itemSchema,\n ),\n )\n .refine(\n items =>\n items.length === ids.size && items.every(({ id }) => ids.has(id)),\n {\n message: 'Items in response do not match request',\n },\n ),\n });\n\n/**\n * Options for {@link PermissionClient} requests.\n *\n * @public\n */\nexport type PermissionClientRequestOptions = {\n token?: string;\n};\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionEvaluator {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n private readonly enableBatchedRequests: boolean;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n\n this.enableBatchedRequests =\n options.config.getOptionalBoolean(\n 'permission.EXPERIMENTAL_enableBatchedRequests',\n ) ?? false;\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorize}\n */\n async authorize(\n requests: AuthorizePermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> {\n if (!this.enabled) {\n return requests.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n if (this.enableBatchedRequests) {\n return this.makeBatchedRequest(requests, options);\n }\n\n return this.makeRequest(\n requests,\n authorizePermissionResponseSchema,\n options,\n );\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorizeConditional}\n */\n async authorizeConditional(\n queries: QueryPermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n return this.makeRequest(queries, queryPermissionResponseSchema, options);\n }\n\n private async makeRequest<TQuery, TResult>(\n queries: TQuery[],\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const request: PermissionMessageBatch<TQuery> = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const parsedResponse = await this.makeRawRequest(\n request,\n itemSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, z.infer<typeof itemSchema>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private async makeBatchedRequest(\n queries: AuthorizePermissionRequest[],\n options?: AuthorizeRequestOptions,\n ) {\n const request: Record<string, BatchedAuthorizePermissionRequest> = {};\n\n for (const query of queries) {\n const { permission, resourceRef } = query;\n\n if (isResourcePermission(permission)) {\n request[permission.name] ||= {\n permission,\n resourceRef: [],\n id: uuid.v4(),\n };\n\n if (resourceRef) {\n request[permission.name].resourceRef?.push(resourceRef);\n }\n } else {\n request[permission.name] ||= {\n permission,\n id: uuid.v4(),\n };\n }\n }\n\n const parsedResponse = await this.makeRawRequest(\n { items: Object.values(request) },\n authorizePermissionResponseBatchSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, (typeof parsedResponse)['items'][number]>);\n\n return queries.map(query => {\n const { id } = request[query.permission.name];\n\n const item = responsesById[id];\n return {\n result: query.resourceRef ? item.result.shift()! : item.result[0],\n };\n });\n }\n\n private async makeRawRequest<TQuery, TResult>(\n request: PermissionMessageBatch<TQuery>,\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n\n return responseSchema(\n itemSchema,\n new Set(request.items.map(({ id }) => id)),\n ).parse(responseBody);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n}\n\n/**\n * @internal\n */\nexport type BatchedAuthorizePermissionRequest = IdentifiedPermissionMessage<\n | {\n permission: BasicPermission;\n resourceRef?: undefined;\n }\n | { permission: ResourcePermission; resourceRef: string[] }\n>;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PermissionClient.esm.js","sources":["../src/PermissionClient.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport * as uuid from 'uuid';\nimport { z } from 'zod';\nimport {\n AuthorizeResult,\n PermissionMessageBatch,\n PermissionCriteria,\n PermissionCondition,\n PermissionEvaluator,\n QueryPermissionRequest,\n AuthorizePermissionRequest,\n AuthorizePermissionResponse,\n QueryPermissionResponse,\n IdentifiedPermissionMessage,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport {\n AuthorizeRequestOptions,\n BasicPermission,\n ResourcePermission,\n} from './types/permission';\nimport { isResourcePermission } from './permissions';\n\nconst permissionCriteriaSchema: z.ZodSchema<\n PermissionCriteria<PermissionCondition>\n> = z.lazy(() =>\n z\n .object({\n rule: z.string(),\n resourceType: z.string(),\n params: z.record(z.any()).optional(),\n })\n .or(z.object({ anyOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ allOf: z.array(permissionCriteriaSchema).nonempty() }))\n .or(z.object({ not: permissionCriteriaSchema })),\n);\n\nconst authorizePermissionResponseSchema: z.ZodSchema<AuthorizePermissionResponse> =\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n });\n\nconst authorizePermissionResponseBatchSchema = z\n .object({\n result: z.array(\n z.union([\n z.literal(AuthorizeResult.ALLOW),\n z.literal(AuthorizeResult.DENY),\n ]),\n ),\n })\n .or(authorizePermissionResponseSchema);\n\nconst queryPermissionResponseSchema: z.ZodSchema<QueryPermissionResponse> =\n z.union([\n z.object({\n result: z\n .literal(AuthorizeResult.ALLOW)\n .or(z.literal(AuthorizeResult.DENY)),\n }),\n z.object({\n result: z.literal(AuthorizeResult.CONDITIONAL),\n pluginId: z.string(),\n resourceType: z.string(),\n conditions: permissionCriteriaSchema,\n }),\n ]);\n\nconst responseSchema = <T>(\n itemSchema: z.ZodSchema<T>,\n ids: Set<string>,\n): z.ZodSchema<PermissionMessageBatch<T>> =>\n z.object({\n items: z\n .array(\n z.intersection(\n z.object({\n id: z.string(),\n }),\n itemSchema,\n ),\n )\n .refine(\n items =>\n items.length === ids.size && items.every(({ id }) => ids.has(id)),\n {\n message: 'Items in response do not match request',\n },\n ),\n });\n\n/**\n * Options for {@link PermissionClient} requests.\n *\n * @public\n */\nexport type PermissionClientRequestOptions = {\n token?: string;\n};\n\n/**\n * An isomorphic client for requesting authorization for Backstage permissions.\n * @public\n */\nexport class PermissionClient implements PermissionEvaluator {\n private readonly enabled: boolean;\n private readonly discovery: DiscoveryApi;\n private readonly enableBatchedRequests: boolean;\n\n constructor(options: { discovery: DiscoveryApi; config: Config }) {\n this.discovery = options.discovery;\n this.enabled =\n options.config.getOptionalBoolean('permission.enabled') ?? false;\n\n this.enableBatchedRequests =\n options.config.getOptionalBoolean(\n 'permission.EXPERIMENTAL_enableBatchedRequests',\n ) ?? false;\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorize}\n */\n async authorize(\n requests: AuthorizePermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<AuthorizePermissionResponse[]> {\n if (!this.enabled) {\n return requests.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n if (this.enableBatchedRequests) {\n return this.makeBatchedRequest(requests, options);\n }\n\n return this.makeRequest(\n requests,\n authorizePermissionResponseSchema,\n options,\n );\n }\n\n /**\n * {@inheritdoc PermissionEvaluator.authorizeConditional}\n */\n async authorizeConditional(\n queries: QueryPermissionRequest[],\n options?: PermissionClientRequestOptions,\n ): Promise<QueryPermissionResponse[]> {\n if (!this.enabled) {\n return queries.map(_ => ({ result: AuthorizeResult.ALLOW as const }));\n }\n\n return this.makeRequest(queries, queryPermissionResponseSchema, options);\n }\n\n private async makeRequest<TQuery, TResult>(\n queries: TQuery[],\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const request: PermissionMessageBatch<TQuery> = {\n items: queries.map(query => ({\n id: uuid.v4(),\n ...query,\n })),\n };\n\n const parsedResponse = await this.makeRawRequest(\n request,\n itemSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, z.infer<typeof itemSchema>>);\n\n return request.items.map(query => responsesById[query.id]);\n }\n\n private async makeBatchedRequest(\n queries: AuthorizePermissionRequest[],\n options?: AuthorizeRequestOptions,\n ) {\n const request: Record<string, BatchedAuthorizePermissionRequest> = {};\n\n for (const query of queries) {\n const { permission, resourceRef } = query;\n\n if (isResourcePermission(permission)) {\n request[permission.name] ||= {\n permission,\n resourceRef: [],\n id: uuid.v4(),\n };\n\n if (resourceRef) {\n request[permission.name].resourceRef?.push(resourceRef);\n }\n } else {\n request[permission.name] ||= {\n permission,\n id: uuid.v4(),\n };\n }\n }\n\n const parsedResponse = await this.makeRawRequest(\n { items: Object.values(request) },\n authorizePermissionResponseBatchSchema,\n options,\n );\n\n const responsesById = parsedResponse.items.reduce((acc, r) => {\n acc[r.id] = r;\n return acc;\n }, {} as Record<string, (typeof parsedResponse)['items'][number]>);\n\n return queries.map(query => {\n const { id } = request[query.permission.name];\n\n const item = responsesById[id];\n\n if (Array.isArray(item.result)) {\n return {\n result: query.resourceRef ? item.result.shift()! : item.result[0],\n };\n }\n return { result: item.result };\n });\n }\n\n private async makeRawRequest<TQuery, TResult>(\n request: PermissionMessageBatch<TQuery>,\n itemSchema: z.ZodSchema<TResult>,\n options?: AuthorizeRequestOptions,\n ) {\n const permissionApi = await this.discovery.getBaseUrl('permission');\n const response = await fetch(`${permissionApi}/authorize`, {\n method: 'POST',\n body: JSON.stringify(request),\n headers: {\n ...this.getAuthorizationHeader(options?.token),\n 'content-type': 'application/json',\n },\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const responseBody = await response.json();\n\n return responseSchema(\n itemSchema,\n new Set(request.items.map(({ id }) => id)),\n ).parse(responseBody);\n }\n\n private getAuthorizationHeader(token?: string): Record<string, string> {\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n}\n\n/**\n * @internal\n */\nexport type BatchedAuthorizePermissionRequest = IdentifiedPermissionMessage<\n | {\n permission: BasicPermission;\n resourceRef?: undefined;\n }\n | { permission: ResourcePermission; resourceRef: string[] }\n>;\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -65,14 +65,6 @@ type ResourcePermission<TResourceType extends string = string> = PermissionBase<
|
|
|
65
65
|
*/
|
|
66
66
|
resourceType: TResourceType;
|
|
67
67
|
}>;
|
|
68
|
-
/**
|
|
69
|
-
* A client interacting with the permission backend can implement this authorizer interface.
|
|
70
|
-
* @public
|
|
71
|
-
* @deprecated Use {@link @backstage/plugin-permission-common#PermissionEvaluator} instead
|
|
72
|
-
*/
|
|
73
|
-
interface PermissionAuthorizer {
|
|
74
|
-
authorize(requests: EvaluatePermissionRequest[], options?: AuthorizeRequestOptions): Promise<EvaluatePermissionResponse[]>;
|
|
75
|
-
}
|
|
76
68
|
/**
|
|
77
69
|
* Options for authorization requests.
|
|
78
70
|
* @public
|
|
@@ -332,6 +324,15 @@ type MetadataResponse = {
|
|
|
332
324
|
rules: MetadataResponseSerializedRule[];
|
|
333
325
|
};
|
|
334
326
|
|
|
327
|
+
/**
|
|
328
|
+
* A client interacting with the permission backend can implement this authorizer interface.
|
|
329
|
+
* @public
|
|
330
|
+
* @deprecated Use {@link @backstage/plugin-permission-common#PermissionEvaluator} instead
|
|
331
|
+
*/
|
|
332
|
+
interface PermissionAuthorizer {
|
|
333
|
+
authorize(requests: EvaluatePermissionRequest[], options?: AuthorizeRequestOptions): Promise<EvaluatePermissionResponse[]>;
|
|
334
|
+
}
|
|
335
|
+
|
|
335
336
|
/**
|
|
336
337
|
* Check if the two parameters are equivalent permissions.
|
|
337
338
|
* @public
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-permission-common",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1-next.0",
|
|
4
4
|
"description": "Isomorphic types and client for Backstage permissions and authorization",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "common-library",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"test": "backstage-cli package test"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@backstage/config": "1.3.
|
|
51
|
+
"@backstage/config": "1.3.3-next.0",
|
|
52
52
|
"@backstage/errors": "1.2.7",
|
|
53
53
|
"@backstage/types": "1.2.1",
|
|
54
54
|
"cross-fetch": "^4.0.0",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"zod-to-json-schema": "^3.20.4"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@backstage/cli": "0.
|
|
60
|
+
"@backstage/cli": "0.33.1-next.1",
|
|
61
61
|
"msw": "^1.0.0"
|
|
62
62
|
},
|
|
63
63
|
"configSchema": "config.d.ts",
|