UNPKG

casbin

Version:

An authorization library that supports access control models like ACL, RBAC, ABAC in Node.JS

215 lines (214 loc) 7.29 kB
// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the 'License'); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an 'AS IS' BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import * as ip from 'ip'; import { isMatch } from 'micromatch'; // regexMatch determines whether key1 matches the pattern of key2 in regular expression. function regexMatch(key1, key2) { return new RegExp(key2).test(key1); } // keyMatch determines whether key1 matches the pattern of key2 (similar to RESTful path), // key2 can contain a *. // For example, '/foo/bar' matches '/foo/*' function keyMatch(key1, key2) { const pos = key2.indexOf('*'); if (pos === -1) { return key1 === key2; } if (key1.length > pos) { return key1.slice(0, pos) === key2.slice(0, pos); } return key1 === key2.slice(0, pos); } // keyMatchFunc is the wrapper for keyMatch. function keyMatchFunc(...args) { const [arg0, arg1] = args; const name1 = (arg0 || '').toString(); const name2 = (arg1 || '').toString(); return keyMatch(name1, name2); } // keyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), // key2 can contain a *. // For example, '/foo/bar' matches '/foo/*', '/resource1' matches '/:resource' function keyMatch2(key1, key2) { key2 = key2.replace(/\/\*/g, '/.*'); const regexp = new RegExp(/(.*):[^/]+(.*)/g); for (;;) { if (!key2.includes('/:')) { break; } key2 = key2.replace(regexp, '$1[^/]+$2'); } return regexMatch(key1, '^' + key2 + '$'); } // keyMatch2Func is the wrapper for keyMatch2. function keyMatch2Func(...args) { const [arg0, arg1] = args; const name1 = (arg0 || '').toString(); const name2 = (arg1 || '').toString(); return keyMatch2(name1, name2); } // keyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. // For example, '/foo/bar' matches '/foo/*', '/resource1' matches '/{resource}' function keyMatch3(key1, key2) { key2 = key2.replace(/\/\*/g, '/.*'); const regexp = new RegExp(/(.*){[^/]+}(.*)/g); for (;;) { if (!key2.includes('/{')) { break; } key2 = key2.replace(regexp, '$1[^/]+$2'); } return regexMatch(key1, key2); } // keyMatch3Func is the wrapper for keyMatch3. function keyMatch3Func(...args) { const [arg0, arg1] = args; const name1 = (arg0 || '').toString(); const name2 = (arg1 || '').toString(); return keyMatch3(name1, name2); } // keyMatch4 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. // Besides what keyMatch3 does, keyMatch4 can also match repeated patterns: // "/parent/123/child/123" matches "/parent/{id}/child/{id}" // "/parent/123/child/456" does not match "/parent/{id}/child/{id}" // But keyMatch3 will match both. function keyMatch4(key1, key2) { key2 = key2.replace(/\/\*/g, '/.*'); const tokens = []; let j = -1; for (let i = 0; i < key2.length; i++) { const c = key2.charAt(i); if (c === '{') { j = i; } else if (c === '}') { tokens.push(key2.substring(j, i + 1)); } } let regexp = new RegExp(/(.*){[^/]+}(.*)/g); for (;;) { if (!key2.includes('/{')) { break; } key2 = key2.replace(regexp, '$1([^/]+)$2'); } regexp = new RegExp('^' + key2 + '$'); let values = regexp.exec(key1); if (!values) { return false; } values = values.slice(1); if (tokens.length !== values.length) { throw new Error('KeyMatch4: number of tokens is not equal to number of values'); } const m = new Map(); tokens.forEach((n, index) => { const key = tokens[index]; let v = m.get(key); if (!v) { v = []; } if (values) { v.push(values[index]); } m.set(key, v); }); for (const value of m.values()) { if (value.length > 1) { for (let i = 1; i < values.length; i++) { if (values[i] !== values[0]) { return false; } } } } return true; } // keyMatch4Func is the wrapper for keyMatch4. function keyMatch4Func(...args) { const [arg0, arg1] = args; const name1 = (arg0 || '').toString(); const name2 = (arg1 || '').toString(); return keyMatch4(name1, name2); } // regexMatchFunc is the wrapper for regexMatch. function regexMatchFunc(...args) { const [arg0, arg1] = args; const name1 = (arg0 || '').toString(); const name2 = (arg1 || '').toString(); return regexMatch(name1, name2); } // ipMatch determines whether IP address ip1 matches the pattern of IP address ip2, // ip2 can be an IP address or a CIDR pattern. // For example, '192.168.2.123' matches '192.168.2.0/24' function ipMatch(ip1, ip2) { // check ip1 if (!(ip.isV4Format(ip1) || ip.isV6Format(ip1))) { throw new Error('invalid argument: ip1 in ipMatch() function is not an IP address.'); } // check ip2 const cidrParts = ip2.split('/'); if (cidrParts.length === 2) { return ip.cidrSubnet(ip2).contains(ip1); } else { if (!(ip.isV4Format(ip2) || ip.isV6Format(ip2))) { console.log(ip2); throw new Error('invalid argument: ip2 in ipMatch() function is not an IP address.'); } return ip.isEqual(ip1, ip2); } } // ipMatchFunc is the wrapper for ipMatch. function ipMatchFunc(...args) { const [arg0, arg1] = args; const ip1 = (arg0 || '').toString(); const ip2 = (arg1 || '').toString(); return ipMatch(ip1, ip2); } /** * Returns true if the specified `string` matches the given glob `pattern`. * * @param string String to match * @param pattern Glob pattern to use for matching. * @returns Returns true if the string matches the glob pattern. * * @example * ```javascript * globMatch("abc.conf", "*.conf") => true * ``` */ function globMatch(string, pattern) { const ok = isMatch(string, pattern); return ok; } // generateGFunction is the factory method of the g(_, _) function. function generateGFunction(rm) { return async function func(...args) { const [arg0, arg1] = args; const name1 = (arg0 || '').toString(); const name2 = (arg1 || '').toString(); if (!rm) { return name1 === name2; } else if (args.length === 2) { return await rm.hasLink(name1, name2); } else { const domain = args[2].toString(); return await rm.hasLink(name1, name2, domain); } }; } export { keyMatchFunc, keyMatch2Func, keyMatch3Func, regexMatchFunc, ipMatchFunc, generateGFunction, keyMatch4Func, globMatch };