Skip to content

Commit

Permalink
Adding IdentityBased and Resource Based Policies (#8)
Browse files Browse the repository at this point in the history
* Adding IdentityBased and ResourceBased Policies

* Support for notAction, notResource, principal and notPrincipal attributes.
  • Loading branch information
roggervalf committed Feb 23, 2020
1 parent 4a5b3e0 commit 58210b7
Show file tree
Hide file tree
Showing 20 changed files with 1,648 additions and 454 deletions.
255 changes: 196 additions & 59 deletions README.md

Large diffs are not rendered by default.

173 changes: 161 additions & 12 deletions example/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { Role } = require('iam-policies');
const { IdentityBasedPolicy, ResourceBasedPolicy } = require('iam-policies');

const role = new Role([
const identityBasedPolicy = new IdentityBasedPolicy([
{
effect: 'allow', // optional, defaults to allow
resource: ['secrets:${user.id}:*'],
Expand All @@ -15,41 +15,182 @@ const role = new Role([
resource: 'secrets:admin:*',
action: 'read',
},
{
resource: 'bd:company:*',
notAction: 'update',
},
{
notResource: ['bd:roles:*'],
action: 'update',
},
]);

const context = { user: { id: 456, bestfriends: [123, 563, 1211] } };

// true
console.log(role.can('read', 'secrets:563:sshhh', context));
console.log(
identityBasedPolicy.can({
action: 'read',
resource: 'secrets:563:sshhh',
context,
})
);
// false
console.log(
identityBasedPolicy.can({
action: 'read',
resource: 'secrets:admin:super-secret',
context,
})
);
// true
console.log(
identityBasedPolicy.can({
action: 'delete',
resource: 'bd:company:account',
context,
})
);
// true
console.log(
identityBasedPolicy.can({
action: 'create',
resource: 'bd:company:account',
context,
})
);
// false
console.log(
identityBasedPolicy.can({
action: 'update',
resource: 'bd:roles:here',
context,
})
);
// true
console.log(
identityBasedPolicy.can({ action: 'update', resource: 'photos', context })
);

const resourceBasedPolicy = new ResourceBasedPolicy([
{
principal: '1',
effect: 'allow',
resource: ['secrets:${user.id}:*'],
action: ['read', 'write'],
},
{
principal: ['1', '2'],
resource: ['secrets:${user.bestfriends}:*'],
action: 'read',
},
{
notPrincipal: { id: '3' },
effect: 'deny',
resource: 'secrets:admin:*',
action: 'read',
},
{
principal: { id: '2' },
resource: 'bd:company:*',
notAction: 'update',
},
{
principal: '3',
notResource: ['bd:roles:*'],
action: 'update',
},
]);

// true
console.log(
resourceBasedPolicy.can({
principal: '1',
action: 'read',
resource: 'secrets:563:sshhh',
context,
})
);
// false
console.log(
resourceBasedPolicy.can({
principal: '1',
action: 'read',
resource: 'secrets:admin:super-secret',
context,
})
);
// false
console.log(role.can('read', 'secrets:admin:super-secret', context));
console.log(
resourceBasedPolicy.can({
principal: '3',
action: 'read',
resource: 'secrets:admin:name',
principalType: 'id',
context,
})
);
// true
console.log(
resourceBasedPolicy.can({
principal: '3',
action: 'create',
resource: 'bd:company:account',
context,
})
);
// false
console.log(
resourceBasedPolicy.can({
principal: '',
action: 'update',
resource: 'bd:roles:here',
context,
})
);
// false
console.log(
resourceBasedPolicy.can({
principal: '',
action: 'update',
resource: 'photos',
context,
})
);

const friendsWithAdminContext = { user: { id: 456, bestfriends: ['admin'] } };

// false
console.log(
role.can('read', 'secrets:admin:super-secret', friendsWithAdminContext)
identityBasedPolicy.can(
{ action: 'read', resource: 'secrets:admin:super-secret' },
friendsWithAdminContext
)
);

const adminRole = new Role([
const adminIdentityBasedPolicy = new IdentityBasedPolicy([
{
resource: '*',
action: '*',
},
]);

// true
console.log(adminRole.can('read', 'someResource'));
console.log(
adminIdentityBasedPolicy.can({ action: 'read', resource: 'someResource' })
);
// true
console.log(adminRole.can('write', 'otherResource'));
console.log(
adminIdentityBasedPolicy.can({ action: 'write', resource: 'otherResource' })
);

const conditions = {
greatherThan: function(data, expected) {
return data > expected;
},
};

const roleWithCondition = new Role(
const identityBasedPolicyWithCondition = new IdentityBasedPolicy(
[
{
effect: 'allow', // optional, defaults to allow
Expand All @@ -67,11 +208,19 @@ const roleWithCondition = new Role(

// true
console.log(
roleWithCondition.can('read', 'secrets:sshhh', { user: { age: 19 } })
identityBasedPolicyWithCondition.can({
action: 'read',
resource: 'secrets:sshhh',
context: { user: { age: 19 } },
})
);
// false
console.log(
roleWithCondition.can('read', 'secrets:admin:super-secret', {
user: { age: 18 },
identityBasedPolicyWithCondition.can({
action: 'read',
resource: 'secrets:admin:super-secret',
context: {
user: { age: 18 },
},
})
);
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "iam-policies-example",
"version": "1.3.1",
"version": "2.0.0",
"license": "MIT",
"dependencies": {
"iam-policies": "link:.."
Expand Down
6 changes: 4 additions & 2 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from "./src/Role";
export * from "./src/Statement";
export * from './src/Statement';
export * from './src/IdentityBasedStatement';
export * from './src/ResourceBasedStatement';
export * from './src/Policies';
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "MIT",
"repository": "Rogger794/iam-policies",
"description": "Identity based policies library",
"version": "1.3.2",
"version": "2.0.0",
"keywords": [
"iam",
"policies",
Expand Down
68 changes: 68 additions & 0 deletions src/IdentityBasedStatement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
Context,
IdentityBasedType,
MatchIdentityBasedInterface
} from './types'
import {
instanceOfResourceBlock,
instanceOfActionBlock
} from './utils'
import {Matcher} from './Matcher'
import {Statement, applyContext} from './Statement'

class IdentityBased extends Statement{
private resource?: string[];
private action?: string[];
private notResource?: string[];
private notAction?: string[];

constructor(identity: IdentityBasedType) {
super(identity);
if(instanceOfResourceBlock(identity)){
this.resource = typeof identity.resource === 'string' ? [identity.resource] : identity.resource;
}else{
this.notResource = typeof identity.notResource === 'string' ? [identity.notResource] : identity.notResource;
}
if(instanceOfActionBlock(identity)){
this.action = typeof identity.action === 'string' ? [identity.action] : identity.action;
}else{
this.notAction = typeof identity.notAction === 'string' ? [identity.notAction] : identity.notAction;
}
}

matches({
action,
resource,
context,
conditionResolver
}:MatchIdentityBasedInterface)
: boolean {
return (
this.matchActions(action,context) &&
this.matchNotActions(action,context) &&
this.matchResources(resource,context) &&
this.matchNotResources(resource,context) &&
this.matchConditions({context,conditionResolver})
);
}

private matchActions( action: string, context?: Context): boolean {
return this.action?this.action.some(a =>
new Matcher(applyContext(a, context)).match(action)):true }

private matchNotActions( action: string, context?: Context): boolean {
return this.notAction?!this.notAction.some(a =>
new Matcher(applyContext(a, context)).match(action)):true }


private matchResources( resource: string, context?: Context): boolean {
return this.resource?this.resource.some(a =>
new Matcher(applyContext(a, context)).match(resource)):true }

private matchNotResources( resource: string, context?: Context): boolean {
return this.notResource?!this.notResource.some(a =>
new Matcher(applyContext(a, context)).match(resource)):true }
}

export { IdentityBased };

0 comments on commit 58210b7

Please sign in to comment.