Made changs to allow params and schemas to be defaulted and required only when there is params defined.

Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com>
Co-authored-by: Mike Lewis <mlewis@spotify.com>

Signed-off-by: Harry Hogg <hhogg@spotify.com>
This commit is contained in:
Harry Hogg
2022-10-06 12:04:31 +01:00
parent a3fef466ef
commit fa40df2bc7
17 changed files with 53 additions and 84 deletions
+1 -1
View File
@@ -303,7 +303,7 @@ export const createCatalogConditionalDecision: (
// @alpha
export const createCatalogPermissionRule: <
TParams extends PermissionRuleParams = PermissionRuleParams,
TParams extends PermissionRuleParams = undefined,
>(
rule: PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>,
) => PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
+4 -2
View File
@@ -177,7 +177,7 @@ export type PermissionCondition<
> = {
resourceType: TResourceType;
rule: string;
params: TParams;
params?: TParams;
};
// @public
@@ -208,7 +208,9 @@ export type PermissionMessageBatch<T> = {
export type PermissionRuleParam = undefined | JsonPrimitive | JsonPrimitive[];
// @public
export type PermissionRuleParams = Record<string, PermissionRuleParam>;
export type PermissionRuleParams =
| undefined
| Record<string, PermissionRuleParam>;
// @public
export type PolicyDecision =
+4 -2
View File
@@ -106,7 +106,7 @@ export type PermissionCondition<
> = {
resourceType: TResourceType;
rule: string;
params: TParams;
params?: TParams;
};
/**
@@ -161,7 +161,9 @@ export type PermissionRuleParam = undefined | JsonPrimitive | JsonPrimitive[];
*
* @public
*/
export type PermissionRuleParams = Record<string, PermissionRuleParam>;
export type PermissionRuleParams =
| undefined
| Record<string, PermissionRuleParam>;
/**
* An individual request sent to the permission backend.
+7 -5
View File
@@ -55,7 +55,9 @@ export type Condition<TRule> = TRule extends PermissionRule<
infer TResourceType,
infer TParams
>
? (params: TParams) => PermissionCondition<TResourceType, TParams>
? undefined extends TParams
? () => PermissionCondition<TResourceType, TParams>
: (params: TParams) => PermissionCondition<TResourceType, TParams>
: never;
// @public
@@ -98,7 +100,7 @@ export const createConditionFactory: <
TParams extends PermissionRuleParams = PermissionRuleParams,
>(
rule: PermissionRule<unknown, unknown, TResourceType, TParams>,
) => (params: TParams) => PermissionCondition<TResourceType, TParams>;
) => (args_0: TParams) => PermissionCondition<TResourceType, TParams>;
// @public
export const createConditionTransformer: <
@@ -129,7 +131,7 @@ export const createPermissionRule: <
TResource,
TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams = PermissionRuleParams,
TParams extends PermissionRuleParams = undefined,
>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) => PermissionRule<TResource, TQuery, TResourceType, TParams>;
@@ -154,7 +156,7 @@ export const makeCreatePermissionRule: <
TResource,
TQuery,
TResourceType extends string,
>() => <TParams extends PermissionRuleParams = PermissionRuleParams>(
>() => <TParams extends PermissionRuleParams = undefined>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) => PermissionRule<TResource, TQuery, TResourceType, TParams>;
@@ -177,7 +179,7 @@ export type PermissionRule<
name: string;
description: string;
resourceType: TResourceType;
paramsSchema: PermissionRuleSchema<TParams>;
paramsSchema?: PermissionRuleSchema<TParams>;
apply(
resource: TResource,
params: NoInfer<z.input<PermissionRuleSchema<TParams>>>,
@@ -36,7 +36,9 @@ export type Condition<TRule> = TRule extends PermissionRule<
infer TResourceType,
infer TParams
>
? (params: TParams) => PermissionCondition<TResourceType, TParams>
? undefined extends TParams
? () => PermissionCondition<TResourceType, TParams>
: (params: TParams) => PermissionCondition<TResourceType, TParams>
: never;
/**
@@ -37,6 +37,7 @@ describe('createConditionFactory', () => {
describe('return value', () => {
it('constructs a condition with the rule name and supplied params', () => {
const conditionFactory = createConditionFactory(testRule);
expect(
conditionFactory({
foo: 'bar',
@@ -36,15 +36,17 @@ import { PermissionRule } from '../types';
*
* @public
*/
export const createConditionFactory =
<
TResourceType extends string,
TParams extends PermissionRuleParams = PermissionRuleParams,
>(
rule: PermissionRule<unknown, unknown, TResourceType, TParams>,
) =>
(params: TParams): PermissionCondition<TResourceType, TParams> => ({
rule: rule.name,
resourceType: rule.resourceType,
params,
});
export const createConditionFactory = <
TResourceType extends string,
TParams extends PermissionRuleParams = PermissionRuleParams,
>(
rule: PermissionRule<unknown, unknown, TResourceType, TParams>,
) => {
return (...args: [TParams]): PermissionCondition<TResourceType, TParams> => {
return {
rule: rule.name,
resourceType: rule.resourceType,
params: args[0],
};
};
};
@@ -47,13 +47,13 @@ const mapConditions = <TQuery>(
}
const rule = getRule(criteria.rule);
const result = rule.paramsSchema.safeParse(criteria.params);
const result = rule.paramsSchema?.safeParse(criteria.params);
if (rule.paramsSchema && !result.success) {
if (result && !result.success) {
throw new InputError(`Parameters to rule are invalid`, result.error);
}
return rule.toQuery(criteria.params);
return rule.toQuery(criteria.params ?? {});
};
/**
@@ -52,11 +52,8 @@ const testRule2 = createPermissionRule({
name: 'test-rule-2',
description: 'Test rule 2',
resourceType: 'test-resource',
paramsSchema: z.object({
foo: z.string().describe('foo'),
}),
apply: (_resource: any, _foo) => false,
toQuery: _foo => ({}),
apply: (_resource: any) => false,
toQuery: () => ({}),
});
describe('createPermissionIntegrationRouter', () => {
@@ -105,7 +102,6 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: { foo: 'b' },
},
],
},
@@ -113,9 +109,6 @@ describe('createPermissionIntegrationRouter', () => {
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'c',
},
},
},
{
@@ -133,9 +126,6 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'b',
},
},
],
},
@@ -153,9 +143,6 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'c',
},
},
],
},
@@ -191,9 +178,6 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'a',
},
},
{
allOf: [
@@ -208,7 +192,6 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: { foo: 'b' },
},
],
},
@@ -227,9 +210,6 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'b',
},
},
],
},
@@ -248,9 +228,6 @@ describe('createPermissionIntegrationRouter', () => {
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'd',
},
},
},
],
@@ -309,9 +286,6 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'b',
},
},
},
{
@@ -337,9 +311,6 @@ describe('createPermissionIntegrationRouter', () => {
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'c',
},
},
},
},
@@ -360,9 +331,6 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: 'd',
},
},
],
},
@@ -626,13 +594,7 @@ describe('createPermissionIntegrationRouter', () => {
paramsSchema: {
$schema: 'http://json-schema.org/draft-07/schema#',
additionalProperties: false,
properties: {
foo: {
description: 'foo',
type: 'string',
},
},
required: ['foo'],
properties: {},
type: 'object',
},
},
@@ -47,7 +47,7 @@ const permissionCriteriaSchema: z.ZodSchema<
z.object({
rule: z.string(),
resourceType: z.string(),
params: z.record(z.any()),
params: z.record(z.any()).optional(),
}),
]),
);
@@ -127,13 +127,13 @@ const applyConditions = <TResourceType extends string, TResource>(
}
const rule = getRule(criteria.rule);
const result = rule.paramsSchema.safeParse(criteria.params);
const result = rule.paramsSchema?.safeParse(criteria.params);
if (rule.paramsSchema && !result.success) {
if (result && !result.success) {
throw new InputError(`Parameters to rule are invalid`, result.error);
}
return rule.apply(resource, criteria.params);
return rule.apply(resource, criteria.params ?? {});
};
/**
@@ -195,7 +195,7 @@ export const createPermissionIntegrationRouter = <
name: rule.name,
description: rule.description,
resourceType: rule.resourceType,
paramsSchema: zodToJsonSchema(rule.paramsSchema),
paramsSchema: zodToJsonSchema(rule.paramsSchema ?? z.object({})),
}));
return res.json({ permissions, rules: serializableRules });
@@ -26,7 +26,7 @@ export const createPermissionRule = <
TResource,
TQuery,
TResourceType extends string,
TParams extends PermissionRuleParams = PermissionRuleParams,
TParams extends PermissionRuleParams = undefined,
>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) => rule;
@@ -41,7 +41,7 @@ export const createPermissionRule = <
*/
export const makeCreatePermissionRule =
<TResource, TQuery, TResourceType extends string>() =>
<TParams extends PermissionRuleParams = PermissionRuleParams>(
<TParams extends PermissionRuleParams = undefined>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) =>
createPermissionRule(rule);
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { z } from 'zod';
import { createPermissionRule } from './createPermissionRule';
import {
createGetRule,
@@ -31,7 +30,6 @@ describe('permission integration utils', () => {
name: 'test-rule-1',
description: 'Test rule 1',
resourceType: 'test-resource',
paramsSchema: z.object({}),
apply: jest.fn(),
toQuery: jest.fn(),
});
@@ -40,7 +38,6 @@ describe('permission integration utils', () => {
name: 'test-rule-2',
description: 'Test rule 2',
resourceType: 'test-resource',
paramsSchema: z.object({}),
apply: jest.fn(),
toQuery: jest.fn(),
});
+2 -2
View File
@@ -64,9 +64,9 @@ export type PermissionRule<
resourceType: TResourceType;
/**
* A ZodSchema that documents the parameters that this rule accepts.
* A ZodSchema that reflects the structure of the parameters that are passed to
*/
paramsSchema: PermissionRuleSchema<TParams>;
paramsSchema?: PermissionRuleSchema<TParams>;
/**
* Apply this rule to a resource already loaded from a backing data source. The params are
+1 -1
View File
@@ -79,7 +79,7 @@ export const playlistConditions: Conditions<{
PlaylistMetadata,
ListPlaylistsFilter,
'playlist-list',
PermissionRuleParams
undefined
>;
}>;
@@ -70,7 +70,7 @@ describe('DefaultPlaylistPermissionPolicy', () => {
playlistConditions.isOwner({
owners: ['user:default/me', 'group:default/owner'],
}),
playlistConditions.isPublic({}),
playlistConditions.isPublic(),
],
},
});
@@ -91,7 +91,7 @@ describe('DefaultPlaylistPermissionPolicy', () => {
playlistConditions.isOwner({
owners: ['user:default/me', 'group:default/owner'],
}),
playlistConditions.isPublic({}),
playlistConditions.isPublic(),
],
},
});
@@ -71,7 +71,7 @@ export class DefaultPlaylistPermissionPolicy implements PermissionPolicy {
playlistConditions.isOwner({
owners: user?.identity.ownershipEntityRefs ?? [],
}),
playlistConditions.isPublic({}),
playlistConditions.isPublic(),
],
});
}
@@ -47,7 +47,6 @@ const isPublic = createPlaylistPermissionRule({
name: 'IS_PUBLIC',
description: 'Should allow only if the playlist is public',
resourceType: PLAYLIST_LIST_RESOURCE_TYPE,
paramsSchema: z.object({}),
apply: (list: PlaylistMetadata) => list.public,
toQuery: () => ({ key: 'public', values: [true] }),
});