Skip to content

Commit

Permalink
feat(condition): extends condition block type to accept unknown values
Browse files Browse the repository at this point in the history
  • Loading branch information
roggervalf committed Nov 10, 2020
2 parents 192ac07 + f3593aa commit 38a6daa
Show file tree
Hide file tree
Showing 20 changed files with 315 additions and 287 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# iam-policies

> [![NPM](https://img.shields.io/npm/v/iam-policies.svg)](https://www.npmjs.com/package/iam-policies) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![Build Status](https://travis-ci.com/roggervalf/iam-policies.svg?branch=master)](https://travis-ci.com/github/roggervalf/iam-policies) [![NPM downloads](https://img.shields.io/npm/dm/iam-policies)](https://www.npmjs.com/package/iam-policies) [![Coverage Status](https://coveralls.io/repos/github/roggervalf/iam-policies/badge.svg?branch=master)](https://coveralls.io/github/roggervalf/iam-policies?branch=master) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
[![NPM](https://img.shields.io/npm/v/iam-policies.svg)](https://www.npmjs.com/package/iam-policies) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![Build Status](https://travis-ci.com/roggervalf/iam-policies.svg?branch=master)](https://travis-ci.com/github/roggervalf/iam-policies) [![NPM downloads](https://img.shields.io/npm/dm/iam-policies)](https://www.npmjs.com/package/iam-policies) [![Coverage Status](https://coveralls.io/repos/github/roggervalf/iam-policies/badge.svg?branch=master)](https://coveralls.io/github/roggervalf/iam-policies?branch=master) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

## About

Expand Down
211 changes: 101 additions & 110 deletions dist/main.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
/**
* Apply the context value in a string.
*
* @param {string} str Pattern string, containing context path.
* @param {object} context Object to get values from path.
* @returns {string} Returns a string with embedded context values.
* @example
* ```javascript
* const context = {
* user: { id: 456, bestFriends: [123, 532, 987] }
* };
* applyContext('secrets:${user.id}:*', context)
* // => 'secrets:456:*'
*
* applyContext('secrets:${user.bestFriends}:*', context)
* // => 'secrets:{123,532,987}:*'
*
* applyContext('secrets:${company.address}:account', context)
* // => 'secrets:undefined:account'
* ```
*/
declare function applyContext<T extends object>(str: string, context?: T): string;

/**
* Gets the value at `path` of `object`. If the resolved value is
* `undefined`, the `defaultValue` is returned in its place.
*
* @since 3.1.0
* @category Object
* @param {Object} object The object to query.
* @param {Array|string} path The path of the property to get.
* @param {*} [defaultValue] The value returned for `undefined` resolved values.
* @returns {*} Returns the resolved value.
* @example
*
* const object = { 'a': [{ 'b': { 'c': 3 } }] }
*
* getValueFromPath(object, 'a[0].b.c')
* // => 3
*
* getValueFromPath(object, ['a', '0', 'b', 'c'])
* // => 3
*
* getValueFromPath(object, 'a.b.c', 'default')
* // => 'default'
*/
declare function getValueFromPath<T, U extends object>(object: U, path: Array<T> | string, defaultValue?: unknown): any;

declare type EffectBlock = 'allow' | 'deny';
declare type Patterns = string[] | string;
interface PrincipalMap {
Expand Down Expand Up @@ -27,16 +75,7 @@ interface OptionalResourceBlock {
interface OptionalNotResourceBlock {
notResource?: Patterns;
}
declare type ConditionKey = string | number | boolean;
interface Context {
[key: string]: ConditionKey | Context | string[] | number[];
}
interface ConditionMap {
[key: string]: ConditionKey[] | ConditionKey;
}
interface ConditionBlock {
[key: string]: ConditionMap;
}
declare type ConditionBlock = Record<string, Record<string, unknown>>;
interface StatementInterface {
sid?: string;
effect?: EffectBlock;
Expand All @@ -46,29 +85,29 @@ declare type Resolver = (data: any, expected: any) => boolean;
interface ConditionResolver {
[key: string]: Resolver;
}
interface MatchConditionInterface {
context?: Context;
interface MatchConditionInterface<T extends object> {
context?: T;
conditionResolver?: ConditionResolver;
}
interface MatchActionBasedInterface extends MatchConditionInterface {
interface MatchActionBasedInterface<T extends object> extends MatchConditionInterface<T> {
action: string;
}
interface MatchIdentityBasedInterface extends MatchActionBasedInterface {
interface MatchIdentityBasedInterface<T extends object> extends MatchActionBasedInterface<T> {
resource: string;
}
interface MatchResourceBasedInterface extends MatchActionBasedInterface {
interface MatchResourceBasedInterface<T extends object> extends MatchActionBasedInterface<T> {
principal?: string;
principalType?: string;
resource?: string;
}
interface EvaluateActionBasedInterface {
interface EvaluateActionBasedInterface<T extends object> {
action: string;
context?: Context;
context?: T;
}
interface EvaluateIdentityBasedInterface extends EvaluateActionBasedInterface {
interface EvaluateIdentityBasedInterface<T extends object> extends EvaluateActionBasedInterface<T> {
resource: string;
}
interface EvaluateResourceBasedInterface extends EvaluateActionBasedInterface {
interface EvaluateResourceBasedInterface<T extends object> extends EvaluateActionBasedInterface<T> {
principal?: string;
principalType?: string;
resource?: string;
Expand All @@ -87,83 +126,35 @@ interface ProxyOptions {
};
}

/**
* Apply the context value in a string.
*
* @param {string} str Pattern string, containing context path.
* @param {object} context Object to get values from path.
* @returns {string} Returns a string with embedded context values.
* @example
* ```javascript
* const context = {
* user: { id: 456, bestFriends: [123, 532, 987] }
* };
* applyContext('secrets:${user.id}:*', context)
* // => 'secrets:456:*'
*
* applyContext('secrets:${user.bestFriends}:*', context)
* // => 'secrets:{123,532,987}:*'
*
* applyContext('secrets:${company.address}:account', context)
* // => 'secrets:undefined:account'
* ```
*/
declare function applyContext(str: string, context?: Context): string;

/**
* Gets the value at `path` of `object`. If the resolved value is
* `undefined`, the `defaultValue` is returned in its place.
*
* @since 3.1.0
* @category Object
* @param {Object} object The object to query.
* @param {Array|string} path The path of the property to get.
* @param {*} [defaultValue] The value returned for `undefined` resolved values.
* @returns {*} Returns the resolved value.
* @example
*
* const object = { 'a': [{ 'b': { 'c': 3 } }] }
*
* getValueFromPath(object, 'a[0].b.c')
* // => 3
*
* getValueFromPath(object, ['a', '0', 'b', 'c'])
* // => 3
*
* getValueFromPath(object, 'a.b.c', 'default')
* // => 'default'
*/
declare function getValueFromPath<T>(object: Record<PropertyKey, unknown>, path: Array<T> | string, defaultValue?: unknown): any;

declare abstract class Statement {
declare abstract class Statement<T extends object> {
protected sid: string;
protected readonly condition?: ConditionBlock;
effect: EffectBlock;
constructor({ sid, effect, condition }: StatementInterface);
matchConditions(this: Statement, { context, conditionResolver }: MatchConditionInterface): boolean;
matchConditions(this: Statement<T>, { context, conditionResolver }: MatchConditionInterface<T>): boolean;
}

declare class ActionBased extends Statement {
declare class ActionBased<T extends object> extends Statement<T> {
private action?;
private notAction?;
private statement;
constructor(action: ActionBasedType);
getStatement(this: ActionBased): ActionBasedType;
matches(this: ActionBased, { action, context, conditionResolver }: MatchActionBasedInterface): boolean;
getStatement(this: ActionBased<T>): ActionBasedType;
matches(this: ActionBased<T>, { action, context, conditionResolver }: MatchActionBasedInterface<T>): boolean;
private checkAndAssignActions;
private matchActions;
private matchNotActions;
}

declare class IdentityBased extends Statement {
declare class IdentityBased<T extends object> extends Statement<T> {
private resource?;
private action?;
private notResource?;
private notAction?;
private statement;
constructor(identity: IdentityBasedType);
getStatement(this: IdentityBased): IdentityBasedType;
matches(this: IdentityBased, { action, resource, context, conditionResolver }: MatchIdentityBasedInterface): boolean;
getStatement(this: IdentityBased<T>): IdentityBasedType;
matches(this: IdentityBased<T>, { action, resource, context, conditionResolver }: MatchIdentityBasedInterface<T>): boolean;
private checkAndAssignActions;
private checkAndAssignResources;
private matchActions;
Expand All @@ -172,7 +163,7 @@ declare class IdentityBased extends Statement {
private matchNotResources;
}

declare class ResourceBased extends Statement {
declare class ResourceBased<T extends object> extends Statement<T> {
private principal?;
private resource?;
private action?;
Expand All @@ -183,8 +174,8 @@ declare class ResourceBased extends Statement {
private hasPrincipals;
private hasResources;
constructor(identity: ResourceBasedType);
getStatement(this: ResourceBased): ResourceBasedType;
matches(this: ResourceBased, { principal, action, resource, principalType, context, conditionResolver }: MatchResourceBasedInterface): boolean;
getStatement(this: ResourceBased<T>): ResourceBasedType;
matches(this: ResourceBased<T>, { principal, action, resource, principalType, context, conditionResolver }: MatchResourceBasedInterface<T>): boolean;
private matchPrincipalAndNotPrincipal;
private matchResourceAndNotResource;
private checkAndAssignActions;
Expand All @@ -198,63 +189,63 @@ declare class ResourceBased extends Statement {
private matchNotResources;
}

declare class Policy {
protected context?: Context;
declare class Policy<T extends object> {
protected context?: T;
protected conditionResolver?: ConditionResolver;
constructor({ context, conditionResolver }: MatchConditionInterface);
setContext(this: Policy, context: Context): void;
getContext(this: Policy): Context | undefined;
setConditionResolver(this: Policy, conditionResolver: ConditionResolver): void;
getConditionResolver(this: Policy): ConditionResolver | undefined;
constructor({ context, conditionResolver }: MatchConditionInterface<T>);
setContext(this: Policy<T>, context: T): void;
getContext(this: Policy<T>): T | undefined;
setConditionResolver(this: Policy<T>, conditionResolver: ConditionResolver): void;
getConditionResolver(this: Policy<T>): ConditionResolver | undefined;
}

interface ActionBasedPolicyInterface {
interface ActionBasedPolicyInterface<T extends object> {
statements: ActionBasedType[];
conditionResolver?: ConditionResolver;
context?: Context;
context?: T;
}
declare class ActionBasedPolicy extends Policy {
declare class ActionBasedPolicy<T extends object> extends Policy<T> {
private denyStatements;
private allowStatements;
private statements;
constructor({ statements, conditionResolver, context }: ActionBasedPolicyInterface);
getStatements(this: ActionBasedPolicy): ActionBasedType[];
evaluate(this: ActionBasedPolicy, { action, context }: EvaluateActionBasedInterface): boolean;
can(this: ActionBasedPolicy, { action, context }: EvaluateActionBasedInterface): boolean;
cannot(this: ActionBasedPolicy, { action, context }: EvaluateActionBasedInterface): boolean;
generateProxy<T extends object, U extends keyof T>(this: ActionBasedPolicy, obj: T, options?: ProxyOptions): T;
constructor({ statements, conditionResolver, context }: ActionBasedPolicyInterface<T>);
getStatements(this: ActionBasedPolicy<T>): ActionBasedType[];
evaluate(this: ActionBasedPolicy<T>, { action, context }: EvaluateActionBasedInterface<T>): boolean;
can(this: ActionBasedPolicy<T>, { action, context }: EvaluateActionBasedInterface<T>): boolean;
cannot(this: ActionBasedPolicy<T>, { action, context }: EvaluateActionBasedInterface<T>): boolean;
generateProxy<U extends object, W extends keyof U>(this: ActionBasedPolicy<T>, obj: U, options?: ProxyOptions): U;
}

interface IdentityBasedPolicyInterface {
interface IdentityBasedPolicyInterface<T extends object> {
statements: IdentityBasedType[];
conditionResolver?: ConditionResolver;
context?: Context;
context?: T;
}
declare class IdentityBasedPolicy extends Policy {
declare class IdentityBasedPolicy<T extends object> extends Policy<T> {
private denyStatements;
private allowStatements;
private statements;
constructor({ statements, conditionResolver, context }: IdentityBasedPolicyInterface);
getStatements(this: IdentityBasedPolicy): IdentityBasedType[];
evaluate(this: IdentityBasedPolicy, { action, resource, context }: EvaluateIdentityBasedInterface): boolean;
can(this: IdentityBasedPolicy, { action, resource, context }: EvaluateIdentityBasedInterface): boolean;
cannot(this: IdentityBasedPolicy, { action, resource, context }: EvaluateIdentityBasedInterface): boolean;
constructor({ statements, conditionResolver, context }: IdentityBasedPolicyInterface<T>);
getStatements(this: IdentityBasedPolicy<T>): IdentityBasedType[];
evaluate(this: IdentityBasedPolicy<T>, { action, resource, context }: EvaluateIdentityBasedInterface<T>): boolean;
can(this: IdentityBasedPolicy<T>, { action, resource, context }: EvaluateIdentityBasedInterface<T>): boolean;
cannot(this: IdentityBasedPolicy<T>, { action, resource, context }: EvaluateIdentityBasedInterface<T>): boolean;
}

interface ResourceBasedPolicyInterface {
interface ResourceBasedPolicyInterface<T extends object> {
statements: ResourceBasedType[];
conditionResolver?: ConditionResolver;
context?: Context;
context?: T;
}
declare class ResourceBasedPolicy extends Policy {
declare class ResourceBasedPolicy<T extends object> extends Policy<T> {
private denyStatements;
private allowStatements;
private statements;
constructor({ statements, conditionResolver, context }: ResourceBasedPolicyInterface);
getStatements(this: ResourceBasedPolicy): ResourceBasedType[];
evaluate(this: ResourceBasedPolicy, { principal, action, resource, principalType, context }: EvaluateResourceBasedInterface): boolean;
can(this: ResourceBasedPolicy, { principal, action, resource, principalType, context }: EvaluateResourceBasedInterface): boolean;
cannot(this: ResourceBasedPolicy, { principal, action, resource, principalType, context }: EvaluateResourceBasedInterface): boolean;
constructor({ statements, conditionResolver, context }: ResourceBasedPolicyInterface<T>);
getStatements(this: ResourceBasedPolicy<T>): ResourceBasedType[];
evaluate(this: ResourceBasedPolicy<T>, { principal, action, resource, principalType, context }: EvaluateResourceBasedInterface<T>): boolean;
can(this: ResourceBasedPolicy<T>, { principal, action, resource, principalType, context }: EvaluateResourceBasedInterface<T>): boolean;
cannot(this: ResourceBasedPolicy<T>, { principal, action, resource, principalType, context }: EvaluateResourceBasedInterface<T>): boolean;
}

export { ActionBased, ActionBasedPolicy, ActionBasedPolicyInterface, IdentityBased, IdentityBasedPolicy, ResourceBased, ResourceBasedPolicy, applyContext, getValueFromPath };
2 changes: 1 addition & 1 deletion dist/main.es.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/main.js.map

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion src/ActionBasedPolicy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ describe('ActionBasedPolicy Class', () => {

describe('when match based on conditions', () => {
it('returns true or false', () => {
class User {
constructor(public info = { id: 456, age: 19 }) {}
get user() {
return this.info;
}
}

const conditionResolver = {
greaterThan: (data: number, expected: number): boolean => {
return data > expected;
Expand Down Expand Up @@ -183,7 +190,7 @@ describe('ActionBasedPolicy Class', () => {
expect(
policy.evaluate({
action: 'write',
context: { user: { id: 456, age: 19 } }
context: new User()
})
).toBe(true);
});
Expand Down
Loading

0 comments on commit 38a6daa

Please sign in to comment.