Skip to content

Commit

Permalink
Merge pull request #3 from Rogger794/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
roggervalf authored Jan 2, 2020
2 parents 0de70c1 + 9fbf497 commit 5f66b2c
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 854 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const role = new Role([
action: ['read', 'write'],
},
{
resource: ['secrets:{${user.bestfriends}}:*'],
resource: ['secrets:${user.bestfriends}:*'],
action: 'read',
},
{
Expand Down
2 changes: 1 addition & 1 deletion example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const role = new Role([
action: ["read", "write"],
},
{
resource: ["secrets:{${user.bestfriends}}:*"],
resource: ["secrets:${user.bestfriends}:*"],
action: "read",
},
{
Expand Down
45 changes: 0 additions & 45 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,6 @@
# yarn lockfile v1


balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=

brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"

concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=

"iam-policies@link:..":
version "0.0.0"
uid ""

lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=

lodash.template@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==
dependencies:
lodash._reinterpolate "^3.0.0"
lodash.templatesettings "^4.0.0"

lodash.templatesettings@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33"
integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==
dependencies:
lodash._reinterpolate "^3.0.0"

minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
9 changes: 2 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"repository": "Rogger794/iam-policies",
"deprecated": false,
"description": "Identity based policies library",
"version": "1.2.0",
"version": "1.3.0",
"keywords": [
"iam",
"policies",
Expand Down Expand Up @@ -46,8 +46,6 @@
"@commitlint/cli": "^8.2.0",
"@commitlint/config-conventional": "^8.2.0",
"@types/jest": "^24.0.22",
"@types/lodash.template": "^4.4.6",
"@types/minimatch": "^3.0.3",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"eslint": "^6.7.2",
Expand All @@ -71,10 +69,7 @@
"files": [
"dist"
],
"dependencies": {
"lodash.template": "^4.5.0",
"minimatch": "^3.0.4"
},
"dependencies": {},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
Expand Down
183 changes: 176 additions & 7 deletions src/Statement.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Minimatch } from 'minimatch'
import template from 'lodash.template'
const reDelimiters = /\$\{([^\}])*\}/g;
const trim = / +(?= )|^\s+|\s+$/g

type StatementEffect = 'allow' | 'deny'
type StatementPattern = string
Expand All @@ -12,6 +12,14 @@ interface StatementConditions {
[key: string]: Condition
}

interface Balance {
start: number,
end: number,
pre: string,
body: string,
post: string
}

export type StatementConfig = {
effect?: StatementEffect
resource: StatementPattern[] | StatementPattern
Expand Down Expand Up @@ -40,10 +48,10 @@ export class Statement {
matches(action: string, resource: string, context?: object, conditionResolvers?: ConditionResolver): boolean {
return (
this.action.some(a =>
new Minimatch(applyContext(a, context)).match(action)
new Matcher(applyContext(a, context)).match(action)
) &&
this.resource.some(r =>
new Minimatch(applyContext(r, context)).match(resource)
new Matcher(applyContext(r, context)).match(resource)
) && ((conditionResolvers&&this.condition&&context)?Object.keys(this.condition).every(condition =>
Object.keys(this.condition?this.condition[condition]:{}).every(path=>
conditionResolvers[condition](getValueFromPath(context,path),this.condition?this.condition[condition][path]:"")
Expand All @@ -60,11 +68,172 @@ export function getValueFromPath(data:any, path:string):any {
if(value){
value=value[step]
}});
if(value instanceof Array)
return `{${value}}`
return value
}

const specialTrim = (str:string):string => str.replace(trim, "");

export function applyContext(str: string, context?: object):string {
if (context == null) return str
const t = template(str)
return t(context)
if (!context) return str
return specialTrim(str.replace(
reDelimiters,
(
match
) => {
let path = match.substr(2,match.length-3);
return match ? String(getValueFromPath(context, path)) : "";
}
))
}

export class Matcher {
private pattern: string
private set: (string|RegExp)[]
private hasSpecialCaracter: boolean
private empty: boolean
constructor(pattern: string) {
this.set = [];
this.pattern = pattern.trim();
this.empty = false;
this.hasSpecialCaracter=false
if (!this.pattern) {
this.empty = true;
}
let set = this.braceExpand();
this.set = set.map((val)=>this.parse(val));
this.set = this.set.filter(s => {
return !s === false;
});
}

braceExpand():string[] {
let pattern = this.pattern
if (!pattern.match(/\{.*\}/)) {
return [pattern];
}
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but only at the top level, so {},a}b will not expand to anything,
// but a{},b}c will be expanded to [a}c,abc].
// One could argue that this is a bug in Bash, but since the goal of
// this module is to match Bash's rules, we escape a leading {}
if (pattern.substr(0, 2) === '{}') {
pattern = '\\{\\}' + pattern.substr(2);
}

return this.expand(pattern, true);
}

parse(pattern:string):string|RegExp {
if (pattern.length > 1024 * 64) {
throw new TypeError("pattern is too long");
}
let regExp,re=""
if (pattern === "") return "";

for (
let i = 0, len = pattern.length, c;
i < len && (c = pattern.charAt(i));
i++
) {

if(c==="*"){
this.hasSpecialCaracter=true
re += "[^/]*?";
}else{
re += c;
}
}

// if the re is not "" at this point, then we need to make sure
// it doesn't match against an empty path part.
// Otherwise a/* will match a/, which it should not.
if (re !== "" && this.hasSpecialCaracter) {
re = "(?=.)" + re;
}

// skip the regexp for non-* patterns
// unescape anything in it, though, so that it'll be
// an exact match.
if (!this.hasSpecialCaracter) {
return pattern.replace(/\\(.)/g, "$1");
}

try {
regExp = new RegExp("^" + re + "$");
} catch (error) {
// If it was an invalid regular expression, then it can't match
// anything.
return new RegExp("$.");
}

return regExp;
}

expand(str:string, isTop?:boolean):string[] {
let expansions = [] as string[];
const balance = balanced('{', '}', str);
if (balance.start<0||/\$$/.test(balance.pre)) return [str];
let parts;

if (!balance.body)
parts = [''];
else
parts = balance.body.split(',')

// no need to expand pre, since it is guaranteed to be free of brace-sets
const pre = balance.pre;
const postParts = balance.post.length? this.expand(balance.post, false): [''];

parts.forEach((part)=>{
postParts.forEach((postPart)=>{
const expansion = pre + part + postPart;
if (!isTop || expansion)
expansions.push(expansion);
})
})

return expansions;
}

match(str:string):boolean {
if (this.empty) return str === "";

const set = this.set;

return set.some(pattern =>
this.matchOne(str, pattern)
)
}

matchOne(str:string, pattern:string|RegExp):boolean {
if (!pattern) return false;

if (typeof pattern === "string") {
return str === pattern;
}
return !!str.match(pattern);
};
}

function balanced(a:string, b:string, str:string):Balance {
const r = range(a, b, str);
return {
start: r[0],
end: r[1],
pre: r[0]>=0?str.slice(0, r[0]):"",
body: r[0]>=0?str.slice(r[0] + a.length, r[1]):"",
post: r[0]>=0?str.slice(r[1] + b.length):""
};
}

function range(a:string, b:string, str:string):number[] {
const left = str.indexOf(a);
const right = str.indexOf(b, left + 1);
if (left >= 0 && right > 0) {
return [ left, right ];
}
return [-1,-1];
}
2 changes: 1 addition & 1 deletion tests/Role.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ it("can match based on context", () => {
action: ["read", "write"],
},
{
resource: ["secrets:{${user.bestfriends}}:*"],
resource: ["secrets:${user.bestfriends}:*"],
action: "read",
},
]);
Expand Down
6 changes: 5 additions & 1 deletion tests/Statement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ it("can match based on context", () => {
};
expect(applyContext("secrets:${user.id}:*", context)).toBe("secrets:456:*");

expect(applyContext("secrets:{${user.bestfriends}}:*", context)).toBe(
expect(applyContext("secrets:${user.bestfriends}:*", context)).toBe(
"secrets:{123,532,987}:*"
);

expect(applyContext("secrets:${user.bestfriends}:account", context)).toBe(
"secrets:{123,532,987}:account"
);
});
Loading

0 comments on commit 5f66b2c

Please sign in to comment.