permissions: rename authorize request and response types to avoid envelope suffix
Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-permission-react': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common.
|
||||
@@ -2,4 +2,6 @@
|
||||
'@backstage/plugin-permission-common': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Authorize API request and response types have been updated. The existing `AuthorizeRequest` and `AuthorizeResponse` types now match the entire request and response objects for the /authorize endpoint, and new types `AuthorizeQuery` and `AuthorizeDecision` have been introduced for individual items in the request and response batches respectively.
|
||||
|
||||
**BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/permission-backend.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-permission-node': minor
|
||||
---
|
||||
|
||||
**BREAKING**: `PolicyAuthorizeRequest` type has been renamed to `PolicyAuthorizeQuery`.
|
||||
**BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common.
|
||||
@@ -29,11 +29,11 @@ import {
|
||||
} from '@backstage/plugin-auth-backend';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
AuthorizeResponse,
|
||||
AuthorizeRequest,
|
||||
AuthorizeDecision,
|
||||
AuthorizeQuery,
|
||||
Identified,
|
||||
AuthorizeRequestEnvelope,
|
||||
AuthorizeResponseEnvelope,
|
||||
AuthorizeRequest,
|
||||
AuthorizeResponse,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
ApplyConditionsRequestEntry,
|
||||
@@ -44,27 +44,27 @@ import { PermissionIntegrationClient } from './PermissionIntegrationClient';
|
||||
import { memoize } from 'lodash';
|
||||
import DataLoader from 'dataloader';
|
||||
|
||||
const requestSchema: z.ZodSchema<AuthorizeRequestEnvelope> = z.object({
|
||||
items: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
resourceRef: z.string().optional(),
|
||||
permission: z.object({
|
||||
name: z.string(),
|
||||
resourceType: z.string().optional(),
|
||||
attributes: z.object({
|
||||
action: z
|
||||
.union([
|
||||
z.literal('create'),
|
||||
z.literal('read'),
|
||||
z.literal('update'),
|
||||
z.literal('delete'),
|
||||
])
|
||||
.optional(),
|
||||
}),
|
||||
}),
|
||||
const querySchema: z.ZodSchema<Identified<AuthorizeQuery>> = z.object({
|
||||
id: z.string(),
|
||||
resourceRef: z.string().optional(),
|
||||
permission: z.object({
|
||||
name: z.string(),
|
||||
resourceType: z.string().optional(),
|
||||
attributes: z.object({
|
||||
action: z
|
||||
.union([
|
||||
z.literal('create'),
|
||||
z.literal('read'),
|
||||
z.literal('update'),
|
||||
z.literal('delete'),
|
||||
])
|
||||
.optional(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
const requestSchema: z.ZodSchema<AuthorizeRequest> = z.object({
|
||||
items: z.array(querySchema),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -81,12 +81,12 @@ export interface RouterOptions {
|
||||
}
|
||||
|
||||
const handleRequest = async (
|
||||
requests: Identified<AuthorizeRequest>[],
|
||||
requests: Identified<AuthorizeQuery>[],
|
||||
user: BackstageIdentityResponse | undefined,
|
||||
policy: PermissionPolicy,
|
||||
permissionIntegrationClient: PermissionIntegrationClient,
|
||||
authHeader?: string,
|
||||
): Promise<Identified<AuthorizeResponse>[]> => {
|
||||
): Promise<Identified<AuthorizeDecision>[]> => {
|
||||
const applyConditionsLoaderFor = memoize((pluginId: string) => {
|
||||
return new DataLoader<
|
||||
ApplyConditionsRequestEntry,
|
||||
@@ -154,8 +154,8 @@ export async function createRouter(
|
||||
router.post(
|
||||
'/authorize',
|
||||
async (
|
||||
req: Request<AuthorizeRequestEnvelope>,
|
||||
res: Response<AuthorizeResponseEnvelope>,
|
||||
req: Request<AuthorizeRequest>,
|
||||
res: Response<AuthorizeResponse>,
|
||||
) => {
|
||||
const token = IdentityClient.getBearerToken(req.header('authorization'));
|
||||
const user = token ? await identity.authenticate(token) : undefined;
|
||||
|
||||
@@ -6,23 +6,7 @@
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
// @public
|
||||
export type AuthorizeRequest = {
|
||||
permission: Permission;
|
||||
resourceRef?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthorizeRequestEnvelope = {
|
||||
items: Identified<AuthorizeRequest>[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthorizeRequestOptions = {
|
||||
token?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthorizeResponse =
|
||||
export type AuthorizeDecision =
|
||||
| {
|
||||
result: AuthorizeResult.ALLOW | AuthorizeResult.DENY;
|
||||
}
|
||||
@@ -32,8 +16,24 @@ export type AuthorizeResponse =
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthorizeResponseEnvelope = {
|
||||
items: Identified<AuthorizeResponse>[];
|
||||
export type AuthorizeQuery = {
|
||||
permission: Permission;
|
||||
resourceRef?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthorizeRequest = {
|
||||
items: Identified<AuthorizeQuery>[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthorizeRequestOptions = {
|
||||
token?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthorizeResponse = {
|
||||
items: Identified<AuthorizeDecision>[];
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -81,18 +81,18 @@ export type PermissionAttributes = {
|
||||
export interface PermissionAuthorizer {
|
||||
// (undocumented)
|
||||
authorize(
|
||||
requests: AuthorizeRequest[],
|
||||
queries: AuthorizeQuery[],
|
||||
options?: AuthorizeRequestOptions,
|
||||
): Promise<AuthorizeResponse[]>;
|
||||
): Promise<AuthorizeDecision[]>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class PermissionClient implements PermissionAuthorizer {
|
||||
constructor(options: { discovery: DiscoveryApi; config: Config });
|
||||
authorize(
|
||||
requests: AuthorizeRequest[],
|
||||
queries: AuthorizeQuery[],
|
||||
options?: AuthorizeRequestOptions,
|
||||
): Promise<AuthorizeResponse[]>;
|
||||
): Promise<AuthorizeDecision[]>;
|
||||
}
|
||||
|
||||
// @public
|
||||
|
||||
@@ -18,7 +18,7 @@ import { RestContext, rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { PermissionClient } from './PermissionClient';
|
||||
import { AuthorizeRequest, AuthorizeResult, Identified } from './types/api';
|
||||
import { AuthorizeQuery, AuthorizeResult, Identified } from './types/api';
|
||||
import { DiscoveryApi } from './types/discovery';
|
||||
import { Permission } from './types/permission';
|
||||
|
||||
@@ -42,7 +42,7 @@ const mockPermission: Permission = {
|
||||
resourceType: 'test-resource',
|
||||
};
|
||||
|
||||
const mockAuthorizeRequest = {
|
||||
const mockAuthorizeQuery = {
|
||||
permission: mockPermission,
|
||||
resourceRef: 'foo',
|
||||
};
|
||||
@@ -54,12 +54,10 @@ describe('PermissionClient', () => {
|
||||
|
||||
describe('authorize', () => {
|
||||
const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => {
|
||||
const responses = req.body.items.map(
|
||||
(a: Identified<AuthorizeRequest>) => ({
|
||||
id: a.id,
|
||||
result: AuthorizeResult.ALLOW,
|
||||
}),
|
||||
);
|
||||
const responses = req.body.items.map((a: Identified<AuthorizeQuery>) => ({
|
||||
id: a.id,
|
||||
result: AuthorizeResult.ALLOW,
|
||||
}));
|
||||
|
||||
return res(json({ items: responses }));
|
||||
});
|
||||
@@ -73,12 +71,12 @@ describe('PermissionClient', () => {
|
||||
});
|
||||
|
||||
it('should fetch entities from correct endpoint', async () => {
|
||||
await client.authorize([mockAuthorizeRequest]);
|
||||
await client.authorize([mockAuthorizeQuery]);
|
||||
expect(mockAuthorizeHandler).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should include a request body', async () => {
|
||||
await client.authorize([mockAuthorizeRequest]);
|
||||
await client.authorize([mockAuthorizeQuery]);
|
||||
|
||||
const request = mockAuthorizeHandler.mock.calls[0][0];
|
||||
|
||||
@@ -93,21 +91,21 @@ describe('PermissionClient', () => {
|
||||
});
|
||||
|
||||
it('should return the response from the fetch request', async () => {
|
||||
const response = await client.authorize([mockAuthorizeRequest]);
|
||||
const response = await client.authorize([mockAuthorizeQuery]);
|
||||
expect(response[0]).toEqual(
|
||||
expect.objectContaining({ result: AuthorizeResult.ALLOW }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not include authorization headers if no token is supplied', async () => {
|
||||
await client.authorize([mockAuthorizeRequest]);
|
||||
await client.authorize([mockAuthorizeQuery]);
|
||||
|
||||
const request = mockAuthorizeHandler.mock.calls[0][0];
|
||||
expect(request.headers.has('authorization')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should include correctly-constructed authorization header if token is supplied', async () => {
|
||||
await client.authorize([mockAuthorizeRequest], { token });
|
||||
await client.authorize([mockAuthorizeQuery], { token });
|
||||
|
||||
const request = mockAuthorizeHandler.mock.calls[0][0];
|
||||
expect(request.headers.get('authorization')).toEqual('Bearer fake-token');
|
||||
@@ -120,7 +118,7 @@ describe('PermissionClient', () => {
|
||||
},
|
||||
);
|
||||
await expect(
|
||||
client.authorize([mockAuthorizeRequest], { token }),
|
||||
client.authorize([mockAuthorizeQuery], { token }),
|
||||
).rejects.toThrowError(/request failed with 401/i);
|
||||
});
|
||||
|
||||
@@ -135,7 +133,7 @@ describe('PermissionClient', () => {
|
||||
},
|
||||
);
|
||||
await expect(
|
||||
client.authorize([mockAuthorizeRequest], { token }),
|
||||
client.authorize([mockAuthorizeQuery], { token }),
|
||||
).rejects.toThrowError(/Unexpected authorization response/i);
|
||||
});
|
||||
|
||||
@@ -143,7 +141,7 @@ describe('PermissionClient', () => {
|
||||
mockAuthorizeHandler.mockImplementationOnce(
|
||||
(req, res, { json }: RestContext) => {
|
||||
const responses = req.body.items.map(
|
||||
(a: Identified<AuthorizeRequest>) => ({
|
||||
(a: Identified<AuthorizeQuery>) => ({
|
||||
id: a.id,
|
||||
outcome: AuthorizeResult.ALLOW,
|
||||
}),
|
||||
@@ -153,14 +151,14 @@ describe('PermissionClient', () => {
|
||||
},
|
||||
);
|
||||
await expect(
|
||||
client.authorize([mockAuthorizeRequest], { token }),
|
||||
client.authorize([mockAuthorizeQuery], { token }),
|
||||
).rejects.toThrowError(/invalid input/i);
|
||||
});
|
||||
|
||||
it('should allow all when permission.enabled is false', async () => {
|
||||
mockAuthorizeHandler.mockImplementationOnce(
|
||||
(req, res, { json }: RestContext) => {
|
||||
const responses = req.body.map((a: Identified<AuthorizeRequest>) => ({
|
||||
const responses = req.body.map((a: Identified<AuthorizeQuery>) => ({
|
||||
id: a.id,
|
||||
result: AuthorizeResult.DENY,
|
||||
}));
|
||||
@@ -172,7 +170,7 @@ describe('PermissionClient', () => {
|
||||
discovery,
|
||||
config: new ConfigReader({ permission: { enabled: false } }),
|
||||
});
|
||||
const response = await disabled.authorize([mockAuthorizeRequest]);
|
||||
const response = await disabled.authorize([mockAuthorizeQuery]);
|
||||
expect(response[0]).toEqual(
|
||||
expect.objectContaining({ result: AuthorizeResult.ALLOW }),
|
||||
);
|
||||
@@ -182,7 +180,7 @@ describe('PermissionClient', () => {
|
||||
it('should allow all when permission.enabled is not configured', async () => {
|
||||
mockAuthorizeHandler.mockImplementationOnce(
|
||||
(req, res, { json }: RestContext) => {
|
||||
const responses = req.body.map((a: Identified<AuthorizeRequest>) => ({
|
||||
const responses = req.body.map((a: Identified<AuthorizeQuery>) => ({
|
||||
id: a.id,
|
||||
outcome: AuthorizeResult.DENY,
|
||||
}));
|
||||
@@ -194,7 +192,7 @@ describe('PermissionClient', () => {
|
||||
discovery,
|
||||
config: new ConfigReader({}),
|
||||
});
|
||||
const response = await disabled.authorize([mockAuthorizeRequest]);
|
||||
const response = await disabled.authorize([mockAuthorizeQuery]);
|
||||
expect(response[0]).toEqual(
|
||||
expect.objectContaining({ result: AuthorizeResult.ALLOW }),
|
||||
);
|
||||
|
||||
@@ -21,13 +21,13 @@ import * as uuid from 'uuid';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
AuthorizeRequest,
|
||||
AuthorizeResponse,
|
||||
AuthorizeQuery,
|
||||
AuthorizeDecision,
|
||||
Identified,
|
||||
PermissionCriteria,
|
||||
PermissionCondition,
|
||||
AuthorizeResponseEnvelope,
|
||||
AuthorizeRequestEnvelope,
|
||||
AuthorizeResponse,
|
||||
AuthorizeRequest,
|
||||
} from './types/api';
|
||||
import { DiscoveryApi } from './types/discovery';
|
||||
import {
|
||||
@@ -98,29 +98,29 @@ export class PermissionClient implements PermissionAuthorizer {
|
||||
* @public
|
||||
*/
|
||||
async authorize(
|
||||
requests: AuthorizeRequest[],
|
||||
queries: AuthorizeQuery[],
|
||||
options?: AuthorizeRequestOptions,
|
||||
): Promise<AuthorizeResponse[]> {
|
||||
): Promise<AuthorizeDecision[]> {
|
||||
// TODO(permissions): it would be great to provide some kind of typing guarantee that
|
||||
// conditional responses will only ever be returned for requests containing a resourceType
|
||||
// but no resourceRef. That way clients who aren't prepared to handle filtering according
|
||||
// to conditions can be guaranteed that they won't unexpectedly get a CONDITIONAL response.
|
||||
|
||||
if (!this.enabled) {
|
||||
return requests.map(_ => ({ result: AuthorizeResult.ALLOW }));
|
||||
return queries.map(_ => ({ result: AuthorizeResult.ALLOW }));
|
||||
}
|
||||
|
||||
const requestEnvelope: AuthorizeRequestEnvelope = {
|
||||
items: requests.map(request => ({
|
||||
const request: AuthorizeRequest = {
|
||||
items: queries.map(query => ({
|
||||
id: uuid.v4(),
|
||||
...request,
|
||||
...query,
|
||||
})),
|
||||
};
|
||||
|
||||
const permissionApi = await this.discovery.getBaseUrl('permission');
|
||||
const response = await fetch(`${permissionApi}/authorize`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(requestEnvelope),
|
||||
body: JSON.stringify(request),
|
||||
headers: {
|
||||
...this.getAuthorizationHeader(options?.token),
|
||||
'content-type': 'application/json',
|
||||
@@ -130,28 +130,28 @@ export class PermissionClient implements PermissionAuthorizer {
|
||||
throw await ResponseError.fromResponse(response);
|
||||
}
|
||||
|
||||
const responseEnvelope = await response.json();
|
||||
this.assertValidResponses(requestEnvelope, responseEnvelope);
|
||||
const responseBody = await response.json();
|
||||
this.assertValidResponse(request, responseBody);
|
||||
|
||||
const responsesById = responseEnvelope.items.reduce((acc, r) => {
|
||||
const responsesById = responseBody.items.reduce((acc, r) => {
|
||||
acc[r.id] = r;
|
||||
return acc;
|
||||
}, {} as Record<string, Identified<AuthorizeResponse>>);
|
||||
}, {} as Record<string, Identified<AuthorizeDecision>>);
|
||||
|
||||
return requestEnvelope.items.map(request => responsesById[request.id]);
|
||||
return request.items.map(query => responsesById[query.id]);
|
||||
}
|
||||
|
||||
private getAuthorizationHeader(token?: string): Record<string, string> {
|
||||
return token ? { Authorization: `Bearer ${token}` } : {};
|
||||
}
|
||||
|
||||
private assertValidResponses(
|
||||
requestEnvelope: AuthorizeRequestEnvelope,
|
||||
private assertValidResponse(
|
||||
request: AuthorizeRequest,
|
||||
json: any,
|
||||
): asserts json is AuthorizeResponseEnvelope {
|
||||
): asserts json is AuthorizeResponse {
|
||||
const authorizedResponses = responseSchema.parse(json);
|
||||
const responseIds = authorizedResponses.items.map(r => r.id);
|
||||
const hasAllRequestIds = requestEnvelope.items.every(r =>
|
||||
const hasAllRequestIds = request.items.every(r =>
|
||||
responseIds.includes(r.id),
|
||||
);
|
||||
if (!hasAllRequestIds) {
|
||||
|
||||
@@ -46,7 +46,7 @@ export enum AuthorizeResult {
|
||||
* An individual authorization request for {@link PermissionClient#authorize}.
|
||||
* @public
|
||||
*/
|
||||
export type AuthorizeRequest = {
|
||||
export type AuthorizeQuery = {
|
||||
permission: Permission;
|
||||
resourceRef?: string;
|
||||
};
|
||||
@@ -55,8 +55,8 @@ export type AuthorizeRequest = {
|
||||
* A batch of authorization requests from {@link PermissionClient#authorize}.
|
||||
* @public
|
||||
*/
|
||||
export type AuthorizeRequestEnvelope = {
|
||||
items: Identified<AuthorizeRequest>[];
|
||||
export type AuthorizeRequest = {
|
||||
items: Identified<AuthorizeQuery>[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -86,7 +86,7 @@ export type PermissionCriteria<TQuery> =
|
||||
* An individual authorization response from {@link PermissionClient#authorize}.
|
||||
* @public
|
||||
*/
|
||||
export type AuthorizeResponse =
|
||||
export type AuthorizeDecision =
|
||||
| { result: AuthorizeResult.ALLOW | AuthorizeResult.DENY }
|
||||
| {
|
||||
result: AuthorizeResult.CONDITIONAL;
|
||||
@@ -97,6 +97,6 @@ export type AuthorizeResponse =
|
||||
* A batch of authorization responses from {@link PermissionClient#authorize}.
|
||||
* @public
|
||||
*/
|
||||
export type AuthorizeResponseEnvelope = {
|
||||
items: Identified<AuthorizeResponse>[];
|
||||
export type AuthorizeResponse = {
|
||||
items: Identified<AuthorizeDecision>[];
|
||||
};
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
export { AuthorizeResult } from './api';
|
||||
export type {
|
||||
AuthorizeQuery,
|
||||
AuthorizeRequest,
|
||||
AuthorizeRequestEnvelope,
|
||||
AuthorizeDecision,
|
||||
AuthorizeResponse,
|
||||
AuthorizeResponseEnvelope,
|
||||
Identified,
|
||||
PermissionCondition,
|
||||
PermissionCriteria,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AuthorizeRequest, AuthorizeResponse } from './api';
|
||||
import { AuthorizeQuery, AuthorizeDecision } from './api';
|
||||
|
||||
/**
|
||||
* The attributes related to a given permission; these should be generic and widely applicable to
|
||||
@@ -48,9 +48,9 @@ export type Permission = {
|
||||
*/
|
||||
export interface PermissionAuthorizer {
|
||||
authorize(
|
||||
requests: AuthorizeRequest[],
|
||||
queries: AuthorizeQuery[],
|
||||
options?: AuthorizeRequestOptions,
|
||||
): Promise<AuthorizeResponse[]>;
|
||||
): Promise<AuthorizeDecision[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AuthorizeRequest } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeDecision } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeQuery } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeRequestOptions } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeResponse } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { BackstageIdentityResponse } from '@backstage/plugin-auth-backend';
|
||||
import { Config } from '@backstage/config';
|
||||
@@ -129,7 +129,7 @@ export const makeCreatePermissionRule: <TResource, TQuery>() => <
|
||||
export interface PermissionPolicy {
|
||||
// (undocumented)
|
||||
handle(
|
||||
request: PolicyAuthorizeRequest,
|
||||
request: PolicyAuthorizeQuery,
|
||||
user?: BackstageIdentityResponse,
|
||||
): Promise<PolicyDecision>;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export type PermissionRule<
|
||||
};
|
||||
|
||||
// @public
|
||||
export type PolicyAuthorizeRequest = Omit<AuthorizeRequest, 'resourceRef'>;
|
||||
export type PolicyAuthorizeQuery = Omit<AuthorizeQuery, 'resourceRef'>;
|
||||
|
||||
// @public
|
||||
export type PolicyDecision =
|
||||
@@ -158,9 +158,9 @@ export type PolicyDecision =
|
||||
export class ServerPermissionClient implements PermissionAuthorizer {
|
||||
// (undocumented)
|
||||
authorize(
|
||||
requests: AuthorizeRequest[],
|
||||
queries: AuthorizeQuery[],
|
||||
options?: AuthorizeRequestOptions,
|
||||
): Promise<AuthorizeResponse[]>;
|
||||
): Promise<AuthorizeDecision[]>;
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ServerPermissionClient } from './ServerPermissionClient';
|
||||
import {
|
||||
Permission,
|
||||
Identified,
|
||||
AuthorizeRequest,
|
||||
AuthorizeQuery,
|
||||
AuthorizeResult,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
@@ -32,7 +32,7 @@ import { RestContext, rest } from 'msw';
|
||||
|
||||
const server = setupServer();
|
||||
const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => {
|
||||
const responses = req.body.items.map((r: Identified<AuthorizeRequest>) => ({
|
||||
const responses = req.body.items.map((r: Identified<AuthorizeQuery>) => ({
|
||||
id: r.id,
|
||||
result: AuthorizeResult.ALLOW,
|
||||
}));
|
||||
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
} from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
AuthorizeRequest,
|
||||
AuthorizeQuery,
|
||||
AuthorizeRequestOptions,
|
||||
AuthorizeResponse,
|
||||
AuthorizeDecision,
|
||||
AuthorizeResult,
|
||||
PermissionClient,
|
||||
PermissionAuthorizer,
|
||||
@@ -78,9 +78,9 @@ export class ServerPermissionClient implements PermissionAuthorizer {
|
||||
}
|
||||
|
||||
async authorize(
|
||||
requests: AuthorizeRequest[],
|
||||
queries: AuthorizeQuery[],
|
||||
options?: AuthorizeRequestOptions,
|
||||
): Promise<AuthorizeResponse[]> {
|
||||
): Promise<AuthorizeDecision[]> {
|
||||
// Check if permissions are enabled before validating the server token. That
|
||||
// way when permissions are disabled, the noop token manager can be used
|
||||
// without fouling up the logic inside the ServerPermissionClient, because
|
||||
@@ -89,9 +89,9 @@ export class ServerPermissionClient implements PermissionAuthorizer {
|
||||
!this.permissionEnabled ||
|
||||
(await this.isValidServerToken(options?.token))
|
||||
) {
|
||||
return requests.map(_ => ({ result: AuthorizeResult.ALLOW }));
|
||||
return queries.map(_ => ({ result: AuthorizeResult.ALLOW }));
|
||||
}
|
||||
return this.permissionClient.authorize(requests, options);
|
||||
return this.permissionClient.authorize(queries, options);
|
||||
}
|
||||
|
||||
private async isValidServerToken(
|
||||
|
||||
@@ -18,6 +18,6 @@ export type {
|
||||
ConditionalPolicyDecision,
|
||||
DefinitivePolicyDecision,
|
||||
PermissionPolicy,
|
||||
PolicyAuthorizeRequest,
|
||||
PolicyAuthorizeQuery,
|
||||
PolicyDecision,
|
||||
} from './types';
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
AuthorizeRequest,
|
||||
AuthorizeQuery,
|
||||
AuthorizeResult,
|
||||
PermissionCondition,
|
||||
PermissionCriteria,
|
||||
@@ -27,13 +27,13 @@ import { BackstageIdentityResponse } from '@backstage/plugin-auth-backend';
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This differs from {@link @backstage/permission-common#AuthorizeRequest} in that `resourceRef`
|
||||
* This differs from {@link @backstage/permission-common#AuthorizeQuery} in that `resourceRef`
|
||||
* should never be provided. This forces policies to be written in a way that's compatible with
|
||||
* filtering collections of resources at data load time.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PolicyAuthorizeRequest = Omit<AuthorizeRequest, 'resourceRef'>;
|
||||
export type PolicyAuthorizeQuery = Omit<AuthorizeQuery, 'resourceRef'>;
|
||||
|
||||
/**
|
||||
* A definitive result to an authorization request, returned by the {@link PermissionPolicy}.
|
||||
@@ -57,7 +57,7 @@ export type DefinitivePolicyDecision = {
|
||||
* conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin
|
||||
* which knows about the referenced permission rules.
|
||||
*
|
||||
* Similar to {@link @backstage/permission-common#AuthorizeResult}, but with the plugin and resource
|
||||
* Similar to {@link @backstage/permission-common#AuthorizeDecision}, but with the plugin and resource
|
||||
* identifiers needed to evaluate the returned conditions.
|
||||
* @public
|
||||
*/
|
||||
@@ -95,7 +95,7 @@ export type PolicyDecision =
|
||||
*/
|
||||
export interface PermissionPolicy {
|
||||
handle(
|
||||
request: PolicyAuthorizeRequest,
|
||||
request: PolicyAuthorizeQuery,
|
||||
user?: BackstageIdentityResponse,
|
||||
): Promise<PolicyDecision>;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { PermissionCriteria } from '@backstage/plugin-permission-common';
|
||||
|
||||
/**
|
||||
* A conditional rule that can be provided in an
|
||||
* {@link @backstage/permission-common#AuthorizeResult} response to an authorization request.
|
||||
* {@link @backstage/permission-common#AuthorizeDecision} response to an authorization request.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
```ts
|
||||
import { ApiRef } from '@backstage/core-plugin-api';
|
||||
import { AuthorizeRequest } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeResponse } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeDecision } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeQuery } from '@backstage/plugin-permission-common';
|
||||
import { ComponentProps } from 'react';
|
||||
import { Config } from '@backstage/config';
|
||||
import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
@@ -24,7 +24,7 @@ export type AsyncPermissionResult = {
|
||||
// @public
|
||||
export class IdentityPermissionApi implements PermissionApi {
|
||||
// (undocumented)
|
||||
authorize(request: AuthorizeRequest): Promise<AuthorizeResponse>;
|
||||
authorize(request: AuthorizeQuery): Promise<AuthorizeDecision>;
|
||||
// (undocumented)
|
||||
static create(options: {
|
||||
config: Config;
|
||||
@@ -35,7 +35,7 @@ export class IdentityPermissionApi implements PermissionApi {
|
||||
|
||||
// @public
|
||||
export type PermissionApi = {
|
||||
authorize(request: AuthorizeRequest): Promise<AuthorizeResponse>;
|
||||
authorize(request: AuthorizeQuery): Promise<AuthorizeDecision>;
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { PermissionApi } from './PermissionApi';
|
||||
import {
|
||||
AuthorizeRequest,
|
||||
AuthorizeResponse,
|
||||
AuthorizeQuery,
|
||||
AuthorizeDecision,
|
||||
PermissionClient,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { Config } from '@backstage/config';
|
||||
@@ -44,7 +44,7 @@ export class IdentityPermissionApi implements PermissionApi {
|
||||
return new IdentityPermissionApi(permissionClient, identity);
|
||||
}
|
||||
|
||||
async authorize(request: AuthorizeRequest): Promise<AuthorizeResponse> {
|
||||
async authorize(request: AuthorizeQuery): Promise<AuthorizeDecision> {
|
||||
const response = await this.permissionClient.authorize([request], {
|
||||
token: await this.identityApi.getIdToken(),
|
||||
});
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
AuthorizeRequest,
|
||||
AuthorizeResponse,
|
||||
AuthorizeQuery,
|
||||
AuthorizeDecision,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { ApiRef, createApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
@@ -27,7 +27,7 @@ import { ApiRef, createApiRef } from '@backstage/core-plugin-api';
|
||||
* @public
|
||||
*/
|
||||
export type PermissionApi = {
|
||||
authorize(request: AuthorizeRequest): Promise<AuthorizeResponse>;
|
||||
authorize(request: AuthorizeQuery): Promise<AuthorizeDecision>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user