From a05a22399cf0c98831a36617e6ae8ffbb4153a30 Mon Sep 17 00:00:00 2001 From: Daniel Jeffery Date: Tue, 12 Dec 2023 15:26:15 -0800 Subject: [PATCH] User validation working --- package-lock.json | 579 +++++++++++++++++------------- package.json | 2 +- server/package-lock.json | 108 ++++++ server/package.json | 1 + server/src/helpers.ts | 36 ++ server/src/openfga-yaml-schema.ts | 325 ++++++++++++++++- server/src/server.common.ts | 215 ++++------- 7 files changed, 873 insertions(+), 393 deletions(-) create mode 100644 server/src/helpers.ts diff --git a/package-lock.json b/package-lock.json index 0052478..aaa70d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@types/webpack-env": "^1.18.4", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^6.14.0", - "@vscode/test-web": "^0.0.49", + "@vscode/test-web": "^0.0.22", "@vscode/vsce": "^2.22.0", "assert-browserify": "^2.0.0", "eslint": "^8.55.0", @@ -198,57 +198,21 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@koa/cors": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-4.0.0.tgz", - "integrity": "sha512-Y4RrbvGTlAaa04DBoPBWJqDR5gPj32OOz827ULXfgB1F7piD1MB/zwn8JR2LAnvdILhxUbXbkXGWuNVsFuVFCQ==", - "dev": true, - "dependencies": { - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 14.0.0" - } - }, "node_modules/@koa/router": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@koa/router/-/router-12.0.1.tgz", - "integrity": "sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-10.1.1.tgz", + "integrity": "sha512-ORNjq5z4EmQPriKbR0ER3k4Gh7YGNhWDL7JBW+8wXDrHLbWYKYSJaOJ9aN06npF5tbTxe2JBOsurpJDAvjiXKw==", + "deprecated": "**IMPORTANT 10x+ PERFORMANCE UPGRADE**: Please upgrade to v12.0.1+ as we have fixed an issue with debuglog causing 10x slower router benchmark performance, see https://github.com/koajs/router/pull/173", "dev": true, "dependencies": { - "debug": "^4.3.4", - "http-errors": "^2.0.0", + "debug": "^4.1.1", + "http-errors": "^1.7.3", "koa-compose": "^4.1.0", "methods": "^1.1.2", - "path-to-regexp": "^6.2.1" + "path-to-regexp": "^6.1.0" }, "engines": { - "node": ">= 12" - } - }, - "node_modules/@koa/router/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@koa/router/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" + "node": ">= 8.0.0" } }, "node_modules/@nodelib/fs.scandir": { @@ -286,17 +250,13 @@ "node": ">= 8" } }, - "node_modules/@playwright/browser-chromium": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.40.1.tgz", - "integrity": "sha512-uNmjHYXBTYTfJkf89D6zVUcesCFzZ/yjkPj8FvBJQ6yf3na/j1rcjVNzx0PzOAGcWKioB/rnWRBi7b5ojOdCHA==", + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, - "hasInstallScript": true, - "dependencies": { - "playwright-core": "1.40.1" - }, "engines": { - "node": ">=16" + "node": ">= 10" } }, "node_modules/@types/eslint": { @@ -560,31 +520,29 @@ "dev": true }, "node_modules/@vscode/test-web": { - "version": "0.0.49", - "resolved": "https://registry.npmjs.org/@vscode/test-web/-/test-web-0.0.49.tgz", - "integrity": "sha512-6L+1SoDQ96O2UtrWjj0se7IVRPHrzVL12YYTkSEQAuftdkcHl3TP5FjlcrlN+TrXWNX9r9jRn7vo/hoItsdQRw==", - "dev": true, - "dependencies": { - "@koa/cors": "^4.0.0", - "@koa/router": "^12.0.1", - "@playwright/browser-chromium": "^1.40.1", - "gunzip-maybe": "^1.4.2", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "koa": "^2.14.2", + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@vscode/test-web/-/test-web-0.0.22.tgz", + "integrity": "sha512-sm4WYidw26eFb1AReC8w5y4aOMdBb5ma5x3ukRJcun9iUB1ajz2nM18rxiYAVimUzrIMQHr9WqC8HYBYP8aNKQ==", + "dev": true, + "dependencies": { + "@koa/router": "^10.1.1", + "decompress": "^4.2.1", + "decompress-targz": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "koa": "^2.13.4", "koa-morgan": "^1.0.1", "koa-mount": "^4.0.0", "koa-static": "^5.0.0", - "minimist": "^1.2.8", - "playwright": "^1.40.1", - "tar-fs": "^3.0.4", - "vscode-uri": "^3.0.8" + "minimist": "^1.2.5", + "playwright": "^1.18.1", + "vscode-uri": "^3.0.3" }, "bin": { "vscode-test-web": "out/index.js" }, "engines": { - "node": ">=16" + "node": ">=8.9.3" } }, "node_modules/@vscode/vsce": { @@ -870,15 +828,15 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "dependencies": { - "debug": "^4.3.4" + "debug": "4" }, "engines": { - "node": ">= 14" + "node": ">= 6.0.0" } }, "node_modules/ajv": { @@ -1106,12 +1064,6 @@ "typed-rest-client": "^1.8.4" } }, - "node_modules/b4a": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", - "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", - "dev": true - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1136,8 +1088,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/basic-auth": { "version": "2.0.1", @@ -1227,15 +1178,6 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "node_modules/browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==", - "dev": true, - "dependencies": { - "pako": "~0.2.0" - } - }, "node_modules/browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", @@ -1287,12 +1229,27 @@ "url": "https://feross.org/support" } ], - "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -1302,6 +1259,12 @@ "node": "*" } }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -1665,6 +1628,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dev": true, + "dependencies": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -1681,6 +1663,83 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "dependencies": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "dependencies": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "dependencies": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "dev": true, + "dependencies": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -1857,18 +1916,6 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2439,12 +2486,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true - }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -2524,6 +2565,15 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2603,8 +2653,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "optional": true + "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -2686,6 +2735,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -2821,23 +2883,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/gunzip-maybe": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz", - "integrity": "sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==", - "dev": true, - "dependencies": { - "browserify-zlib": "^0.1.4", - "is-deflate": "^1.0.0", - "is-gzip": "^1.0.0", - "peek-stream": "^1.1.0", - "pumpify": "^1.3.3", - "through2": "^2.0.3" - }, - "bin": { - "gunzip-maybe": "bin.js" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -2998,29 +3043,30 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">= 14" + "node": ">= 6" } }, "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "6", "debug": "4" }, "engines": { - "node": ">= 14" + "node": ">= 6" } }, "node_modules/ieee754": { @@ -3041,8 +3087,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/ignore": { "version": "5.2.4", @@ -3252,12 +3297,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-deflate": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-deflate/-/is-deflate-1.0.0.tgz", - "integrity": "sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==", - "dev": true - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3303,15 +3342,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-gzip": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", - "integrity": "sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -3328,6 +3358,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "dev": true + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -3422,6 +3458,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -3916,6 +3961,27 @@ "node": ">=10" } }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/markdown-it": { "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", @@ -4064,7 +4130,8 @@ "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true + "dev": true, + "optional": true }, "node_modules/mocha": { "version": "10.2.0", @@ -4343,6 +4410,15 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -4533,12 +4609,6 @@ "node": ">=6" } }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "dev": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4657,17 +4727,6 @@ "node": ">=8" } }, - "node_modules/peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -4692,6 +4751,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -4916,27 +5005,7 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, + "optional": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -4986,12 +5055,6 @@ } ] }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -5336,6 +5399,25 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dev": true, + "dependencies": { + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -5535,22 +5617,6 @@ "node": ">= 0.6" } }, - "node_modules/stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "node_modules/streamx": { - "version": "2.15.6", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", - "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", - "dev": true, - "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -5646,6 +5712,15 @@ "node": ">=4" } }, + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dev": true, + "dependencies": { + "is-natural-number": "^4.0.1" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5691,26 +5766,32 @@ "node": ">=6" } }, - "node_modules/tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, "dependencies": { - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^3.1.5" + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/tar-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", - "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "node_modules/tar-stream/node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "dev": true, "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" } }, "node_modules/terser": { @@ -5786,15 +5867,11 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true }, "node_modules/tmp": { "version": "0.2.1", @@ -5808,6 +5885,12 @@ "node": ">=8.17.0" } }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6122,6 +6205,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/underscore": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", diff --git a/package.json b/package.json index 75f4bb2..9a9d27f 100644 --- a/package.json +++ b/package.json @@ -170,7 +170,7 @@ "@types/webpack-env": "^1.18.4", "@typescript-eslint/eslint-plugin": "^6.14.0", "@typescript-eslint/parser": "^6.14.0", - "@vscode/test-web": "^0.0.49", + "@vscode/test-web": "^0.0.22", "@vscode/vsce": "^2.22.0", "assert-browserify": "^2.0.0", "eslint": "^8.55.0", diff --git a/server/package-lock.json b/server/package-lock.json index f63093e..276fd75 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -9,6 +9,7 @@ "version": "0.2.12", "license": "Apache-2.0", "dependencies": { + "@openfga/sdk": "^0.3.0", "@openfga/syntax-transformer": "^0.2.0-beta.7", "ajv": "^8.12.0", "vscode-languageserver": "^9.0.1", @@ -22,6 +23,18 @@ "node": "*" } }, + "node_modules/@openfga/sdk": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@openfga/sdk/-/sdk-0.3.0.tgz", + "integrity": "sha512-WPyvEyktsSCueUQgA1P91qFMFyOMCfXDKOoOXSbubQtmAqtGiFs8xPq496lEfde48E0YF6scAdcVfFbqU8sQfQ==", + "dependencies": { + "axios": "^1.6.2", + "tiny-async-pool": "^2.1.0" + }, + "engines": { + "node": ">=14.17.0" + } + }, "node_modules/@openfga/syntax-transformer": { "version": "0.2.0-beta.7", "resolved": "https://registry.npmjs.org/@openfga/syntax-transformer/-/syntax-transformer-0.2.0-beta.7.tgz", @@ -59,16 +72,106 @@ "node": ">=16" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -85,6 +188,11 @@ "node": ">=0.10.0" } }, + "node_modules/tiny-async-pool": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-2.1.0.tgz", + "integrity": "sha512-ltAHPh/9k0STRQqaoUX52NH4ZQYAJz24ZAEwf1Zm+HYg3l9OXTWeqWKyYsHu40wF/F0rxd2N2bk5sLvX2qlSvg==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/server/package.json b/server/package.json index 12586cf..11c32b9 100644 --- a/server/package.json +++ b/server/package.json @@ -12,6 +12,7 @@ "url": "https://github.com/openfga/vscode-ext" }, "dependencies": { + "@openfga/sdk": "^0.3.0", "@openfga/syntax-transformer": "^0.2.0-beta.7", "ajv": "^8.12.0", "vscode-languageserver": "^9.0.1", diff --git a/server/src/helpers.ts b/server/src/helpers.ts new file mode 100644 index 0000000..2d57c7f --- /dev/null +++ b/server/src/helpers.ts @@ -0,0 +1,36 @@ +import { Position, Range } from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; + +export function getRangeOfWord(document: TextDocument, position: Position): Range { + const text = document.getText(); + + let pointerStart = document.offsetAt(position); + let pointerEnd = document.offsetAt(position); + + while (text.charAt(pointerStart).match(/\w/)) { + pointerStart--; + } + + while (text.charAt(pointerEnd).match(/\w/)) { + pointerEnd++; + } + + let start = document.positionAt(pointerStart + 1); + let end = document.positionAt(pointerEnd); + + if ( + document.getText({ start, end }) === "but" && + // If we've hovered "but", track forward and check if there is a matching "not" + document.getText({ start, end: document.positionAt(pointerEnd + 4) }) === "but not" + ) { + end = document.positionAt(pointerEnd + 4); + } else if ( + document.getText({ start, end }) === "not" && + // If we've hovered "not", track backward and check if there is a matching "but" + document.getText({ start: document.positionAt(pointerStart - 3), end }) === "but not" + ) { + start = document.positionAt(pointerStart - 3); + } + + return { start, end }; +} diff --git a/server/src/openfga-yaml-schema.ts b/server/src/openfga-yaml-schema.ts index 93fd4b8..f2b6b03 100644 --- a/server/src/openfga-yaml-schema.ts +++ b/server/src/openfga-yaml-schema.ts @@ -1,32 +1,330 @@ -import { Schema } from "ajv"; +import { AuthorizationModel, Condition, TypeDefinition, Userset } from "@openfga/sdk"; +import Ajv, { Schema, ValidateFunction, SchemaValidateFunction } from "ajv"; const identifier = "[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?"; -export const USER = (user: string): boolean => { - if (!user.match(new RegExp(`^${identifier}:(\\*|${identifier}(#${identifier})?)$`)) || user.length > 256) { +type Tuple = { + user: string, + relation: string, + object: string, + condition?: { + name: string, + context?: {[param: string]: number | string}, + } +} + +// Errors for tuples validation +const invalidType = (user: string, types: string[], instancePath: string) => { + return { + keyword: 'valid_tuple', + message: `Invalid type '${user}'. Valid types are [${types}]`, + instancePath: instancePath + "/user", + } +} + +const relationMustExistOnType = (relation: string, type: string, instancePath: string) => { + return { + keyword: 'valid_tuple', + message: `Relation '${relation}' must exist on type '${type}'.`, + instancePath: instancePath + "/relation", + } +} + +const userNotTypeRestriction = (user: string, tuple: Tuple, instancePath: string) => { + return { + keyword: 'valid_tuple', + message: `'${user}' is not a type restriction on relation '${tuple.relation}' of type '${tuple.object.split(":")[0]}'.`, + instancePath: instancePath + "/user", + } +} + +const conditionNotTypeRestriction = (user: string, tuple: Tuple, instancePath: string) => { + return { + keyword: 'valid_tuple', + message: `'${user}' with condition '${tuple.condition?.name}' not a type restriction on '${tuple.relation}' of type '${tuple.object.split(":")[0]}'.`, + instancePath: instancePath + "/condition/name", + } +} + +const notAParameter = (param: string, tuple: Tuple, instancePath: string) => { + return { + keyword: 'valid_tuple', + message: `'${param}' is not a parameter on condition '${tuple.condition?.name}'.`, + instancePath: instancePath + `/condition/context/${param}`, + } +} + + +// Format enforcement + +function formatUser(user: string): boolean { + if (!user.match(new RegExp(`^${identifier}:(\\*|${identifier}(#${identifier})?)$`)) || user.length > 512) { return false; } return true; -}; +} -export const RELATION = (relation: string): boolean => { - return relation.match(new RegExp(`^${identifier}$`)) !== null; -}; +function formatRelation(relation: string): boolean { + if (!relation.match(new RegExp(`^${identifier}$`)) || relation.length > 50) { + return false; + } + return true; +} + +function formatObject(object: string): boolean { + if (!object.match(new RegExp(`^${identifier}:${identifier}$`)) || object.length > 256) { + return false; + } + return true; +} + +function formatCondition(condition: string): boolean { + if (!condition.match(new RegExp(`^${identifier}$`)) || condition.length > 50) { + return false; + } + return true; +} + +// Validation for Types + +function validateTypes(tuple: Tuple, types: string[], instancePath: string): boolean { + const errors = []; + + const user = tuple.user.split(":")[0]; + + // Ensure valid type of user + if (!types.includes(user)) { + errors.push(invalidType(user, types, instancePath)); + } + + const object = tuple.object.split(":")[0]; + + // Ensure valid type of object + if (!types.includes(object)) { + errors.push(invalidType(object, types, instancePath)); + } + + // Report all errors + if (errors.length) { + validateTuple.errors?.push(...errors); + return false; + } + return true; +} + +// Validate Relation +function validateRelation( + tuple: Tuple, + typeDefs: TypeDefinition[], + instancePath: string): boolean { + const errors = []; + + // Check if relation exists on given type + let doesExisitOnType = false; + if (tuple.user.includes("#")) { + const user = tuple.user.split(":")[0]; + const memberRelation = tuple.user.split("#")[1]; + for (const t of typeDefs) { + if (t !== undefined && t.relations && t.type === user && t.relations[memberRelation]) { + doesExisitOnType = true; + break; + } + } + + if (!doesExisitOnType) { + errors.push(relationMustExistOnType(memberRelation, user, instancePath)); + } + } + + // Check if relation exists on given object + const objectType = tuple.object.split(":")[0]; + let doesExisitOnObject = false; + for (const t of typeDefs) { + if (t !== undefined && t.relations && t.type === objectType && t.relations[tuple.relation]) { + doesExisitOnObject = true; + break; + } + } + if (!doesExisitOnObject) { + errors.push(relationMustExistOnType(tuple.relation, objectType, instancePath)); + } -export const OBJECT = (object: string): boolean => { - if (!object.match(new RegExp(`^${identifier}:${identifier}$`))) { + if (errors.length) { + validateTuple.errors?.push(...errors); return false; } + return true; +} - const parts = object.split(":"); +function validateTypeRestrictions( + tuple: Tuple, + typeDefs: TypeDefinition[], + conditions: {[key: string]: Condition} | undefined, + instancePath: string): boolean { - if (parts[0].length > 128 || parts[1].length > 128) { + validateTuple.errors = validateTuple.errors || []; + + // Wildcard + if (tuple.user.includes(":*")) { + const type = typeDefs.filter(t => t.type === tuple.object.split(":")[0])[0]; + const user = tuple.user.split(":")[0] + + if (type.relations && type.relations[tuple.relation] && + type.metadata?.relations && type.metadata.relations[tuple.relation]) { + const userTypes = type.metadata.relations[tuple.relation].directly_related_user_types; + if (userTypes?.filter(t => t.type === user && t.wildcard && !t.condition).length) { + + if (tuple.condition) { + validateTuple.errors.push(conditionNotTypeRestriction(`${user}:*`, tuple, instancePath)); + return false; + } + + return true; + } else if (userTypes?.filter(t => t.type === user && t.wildcard && t.condition).length) { + const condition = userTypes?.filter(t => t.type === user && t.wildcard && t.condition)[0].condition; + if (condition && conditions && conditions[condition].name === tuple.condition?.name) { + return validateConditionParams(tuple, condition, conditions, instancePath); + } + + validateTuple.errors.push(conditionNotTypeRestriction(`${user}:*`, tuple, instancePath)); + return false; + } + } + + validateTuple.errors.push(userNotTypeRestriction(`${user}:*`, tuple, instancePath)); + return false; + } + + // Member + if (tuple.user.includes("#")) { + const type = typeDefs.filter(t => t.type === tuple.object.split(":")[0])[0]; + const user = tuple.user.split(":")[0]; + const relation = tuple.user.split("#")[1]; + + if (type.relations && type.relations[tuple.relation] && + type.metadata?.relations && type.metadata.relations[tuple.relation]) { + const userTypes = type.metadata.relations[tuple.relation].directly_related_user_types; + if (userTypes?.filter(t => t.type === user && t.relation === relation && !t.condition).length) { + + if (tuple.condition) { + validateTuple.errors.push(conditionNotTypeRestriction(`${user}#${relation}`, tuple, instancePath)); + return false; + } + + return true; + } else if (userTypes?.filter(t => t.type === user && t.relation === relation && t.condition).length) { + const condition = userTypes?.filter(t => t.type === user && t.relation === relation && t.condition)[0].condition; + if (condition && conditions && conditions[condition].name === tuple.condition?.name) { + return validateConditionParams(tuple, condition, conditions, instancePath); + } + + validateTuple.errors.push(conditionNotTypeRestriction(`${user}#${relation}`, tuple, instancePath)); + return false; + } + } + + validateTuple.errors.push(userNotTypeRestriction(`${user}#${relation}`, tuple, instancePath)); + return false; + } + + // Type + const type = typeDefs.filter(t => t.type === tuple.object.split(":")[0])[0]; + const user = tuple.user.split(":")[0]; + + if (type.relations && type.relations[tuple.relation] && + type.metadata?.relations && type.metadata.relations[tuple.relation]) { + const userTypes = type.metadata.relations[tuple.relation].directly_related_user_types; + if (userTypes?.filter(t => t.type === user && !t.wildcard && !t.relation && !t.condition).length) { + + if (tuple.condition) { + validateTuple.errors.push(conditionNotTypeRestriction(user, tuple, instancePath)); + return false; + } + + return true; + } else if (userTypes?.filter(t => t.type === user && !t.wildcard && !t.relation && t.condition).length) { + const condition = userTypes?.filter(t => t.type === user && !t.relation && t.condition)[0].condition; + if (condition && conditions && conditions[condition].name === tuple.condition?.name) { + return validateConditionParams(tuple, condition, conditions, instancePath); + } + + validateTuple.errors.push(conditionNotTypeRestriction(user, tuple, instancePath)); + return false; + } + + validateTuple.errors.push(userNotTypeRestriction(`${user}`, tuple, instancePath)); return false; } return true; } -export const OPENFGA_YAML_SCHEMA: Schema = { + +function validateConditionParams( + tuple: Tuple, + condition: string, + conditions: { [key: string]: Condition; }, + instancePath: string): boolean { + + validateTuple.errors = validateTuple.errors || []; + + if (tuple.condition && tuple.condition.context && conditions[condition].parameters) { + for (const p of Object.keys(tuple.condition.context)) { + if (!conditions[condition].parameters![p]) { + validateTuple.errors.push(notAParameter(p, tuple, instancePath)); + return false; + } + } + } + return true; +} + +// Validation for tuples +const validateTuple: SchemaValidateFunction = function (tuple: Tuple, cxt: {instancePath: string}): boolean { + validateTuple.errors = validateTuple.errors || []; + + // @ts-ignore + const jsonModel: AuthorizationModel = this.jsonModel; + + // Validate + const types = jsonModel.type_definitions.map(d => d.type); + return ( + validateTypes(tuple, types, cxt.instancePath) && + validateRelation(tuple, jsonModel.type_definitions, cxt.instancePath) && + validateTypeRestrictions(tuple, jsonModel.type_definitions, jsonModel.conditions, cxt.instancePath) + ) +}; + +export function YamlStoreValidator(): ValidateFunction { + // YAML validation + return new Ajv({ + allErrors: true, + verbose: true, + passContext: true, + $data: true, + }) + .addFormat("user", { + validate: formatUser, + }) + .addFormat("relation", { + validate: formatRelation, + }) + .addFormat("object", { + validate: formatObject, + }) + .addFormat("condition", { + validate: formatCondition, + }) + .addKeyword({ + keyword: "valid_tuple", + type: "object", + schema: false, + errors: true, + validate: validateTuple, + }) + .compile(OPENFGA_YAML_SCHEMA); +} + +const OPENFGA_YAML_SCHEMA: Schema = { type: "object", required: ["tests"], additionalProperties: false, @@ -77,6 +375,7 @@ export const OPENFGA_YAML_SCHEMA: Schema = { properties: { name: { type: "string", + format: "condition", }, context: { type: "object", @@ -84,6 +383,7 @@ export const OPENFGA_YAML_SCHEMA: Schema = { }, }, }, + valid_tuple: true, }, }, tests: { @@ -134,6 +434,7 @@ export const OPENFGA_YAML_SCHEMA: Schema = { properties: { name: { type: "string", + format: "condition", }, context: { type: "object", diff --git a/server/src/server.common.ts b/server/src/server.common.ts index 00704c5..4284151 100644 --- a/server/src/server.common.ts +++ b/server/src/server.common.ts @@ -10,8 +10,6 @@ import { Hover, MarkupContent, MarkupKind, - Range, - Position, CodeActionParams, CodeAction, DocumentUri, @@ -19,15 +17,16 @@ import { import { TextDocument } from "vscode-languageserver-textdocument"; -import { validator, errors, transformer} from "@openfga/syntax-transformer"; +import { validator, errors, transformer } from "@openfga/syntax-transformer"; import { defaultDocumentationMap } from "./documentation"; import { getDuplicationFix, getMissingDefinitionFix, getReservedTypeNameFix } from "./code-action"; import { LineCounter, Node, parseDocument } from "yaml"; import { BlockMap, SourceToken } from "yaml/dist/parse/cst"; import { YAMLSourceMap, rangeFromLinePos } from "./yaml-utils"; -import Ajv, { ErrorObject, ValidateFunction } from "ajv"; -import { OBJECT, OPENFGA_YAML_SCHEMA, RELATION, USER } from "./openfga-yaml-schema"; +import { ErrorObject, ValidateFunction } from "ajv"; +import { YamlStoreValidator } from "./openfga-yaml-schema"; +import { getRangeOfWord } from "./helpers"; export function startServer(connection: _Connection) { console.log = connection.console.log.bind(connection.console); @@ -40,8 +39,7 @@ export function startServer(connection: _Connection) { let hasWorkspaceFolderCapability = false; let hasDiagnosticRelatedInformationCapability = false; - let schemaValidator: ValidateFunction; - //let schemaValidator: Ajv; + const schemaValidator: ValidateFunction = YamlStoreValidator(); connection.onInitialize((params: InitializeParams) => { console.log("Initialize openfga language server"); @@ -81,15 +79,6 @@ export function startServer(connection: _Connection) { }); connection.onInitialized(() => { - // Once initialized, setup validator - schemaValidator = new Ajv({ - allErrors: true, - verbose: true, - }) - .addFormat("user", USER) - .addFormat("relation", RELATION) - .addFormat("object", OBJECT) - .compile(OPENFGA_YAML_SCHEMA); }); connection.onDidChangeConfiguration(() => { @@ -170,7 +159,11 @@ export function startServer(connection: _Connection) { connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } - function validateYamlSyntaxAndModel(textDocument: TextDocument): Diagnostic[] { + function validateYamlSyntaxAndModel(textDocument: TextDocument): void { + connection.sendDiagnostics({ + uri: textDocument.uri, + diagnostics: [], + }); const diagnostics: Diagnostic[] = []; const lineCounter = new LineCounter(); @@ -179,10 +172,6 @@ export function startServer(connection: _Connection) { keepSourceTokens: true, }); - if (yamlDoc.has("model")) { - console.dir(transformer.transformDSLToJSONObject(yamlDoc.get("model") as string), {depth:null}); - } - const map = new YAMLSourceMap(); map.doMap(yamlDoc.contents); @@ -191,104 +180,90 @@ export function startServer(connection: _Connection) { diagnostics.push({ message: err.message, range: rangeFromLinePos(err.linePos) }); } - // If no diagnostics, continue parsing. - if (!diagnostics.length && !schemaValidator(yamlDoc.toJSON())) { - schemaValidator.errors?.forEach((e: ErrorObject) => { - let start = { line: 0, character: 0 }; - let end = { line: 0, character: 0 }; - let message; - - if (e.keyword === "additionalProperties") { - // If we've got invalid keys, mark them - let key = e.params["additionalProperty"]; - if (e.instancePath) { - const path = e.instancePath.substring(1).replace(/\//g, "."); - key = path.concat(".", key); - } - const range = map.nodes.get(key); - if (range) { - start = textDocument.positionAt(range?.[0]); - end = textDocument.positionAt(range?.[1]); - } - message = key + " is not a recognized key."; - } else if (e.keyword === "required") { - const key = e.instancePath.substring(1).split("/"); - let range; - if (/^\d+$/.test(key[key.length - 1])) { - // We have an array if the key is a number - const value = yamlDoc.getIn(key); - range = (value as Node).range; - } else { - range = map.nodes.get(key.join(".")); - } - if (range) { - start = textDocument.positionAt(range?.[0]); - end = textDocument.positionAt(range?.[1]); - } + if (yamlDoc.has("model")) { + let position: { line: number; col: number }; - message = key.join(".") + " " + e.message; - } else { - // All other schema errors - const key = e.instancePath.substring(1).replace(/\//g, "."); - const range = map.nodes.get(key); - if (range) { - start = textDocument.positionAt(range?.[0]); - end = textDocument.positionAt(range?.[1]); - } - message = key + " " + e.message; + // Get the model token and find its position + (yamlDoc.contents?.srcToken as BlockMap).items.forEach((i) => { + if (i.key?.offset !== undefined && (i.key as SourceToken).source === "model") { + position = lineCounter.linePos(i.key?.offset); } - diagnostics.push({ message: message, range: { start, end } }); }); + + // Shift generated diagnostics by line of model, and indent of 2 + let dslDiagnostics = getDiagnosticsForDsl(yamlDoc.get("model") as string); + dslDiagnostics = dslDiagnostics.map((d) => { + const r = d.range; + r.start.line += position.line; + r.start.character += 2; + r.end.line += position.line; + r.end.character += 2; + return d; + }); + diagnostics.push(...dslDiagnostics); } - // Finally validate openfga model - if (!diagnostics.length) { - // Get location of model in CST - if (yamlDoc.has("model")) { - let position: { line: number; col: number }; + try { - // Get the model token and find its position - (yamlDoc.contents?.srcToken as BlockMap).items.forEach((i) => { - if (i.key?.offset !== undefined && (i.key as SourceToken).source === "model") { - position = lineCounter.linePos(i.key?.offset); + const jsonModel = transformer.transformDSLToJSONObject(yamlDoc.get("model") as string); + + if (jsonModel && !schemaValidator.call({ jsonModel }, yamlDoc.toJSON())) { + schemaValidator.errors?.forEach((e: ErrorObject) => { + let start = { line: 0, character: 0 }; + let end = { line: 0, character: 0 }; + let message; + + if (e.keyword === "additionalProperties") { + // If we've got invalid keys, mark them + let key = e.params["additionalProperty"]; + if (e.instancePath) { + const path = e.instancePath.substring(1).replace(/\//g, "."); + key = path.concat(".", key); + } + const range = map.nodes.get(key); + if (range) { + start = textDocument.positionAt(range?.[0]); + end = textDocument.positionAt(range?.[1]); + } + message = key + " is not a recognized key."; + } else if (e.keyword === "required" || e.keyword === "valid_tuple") { + const key = e.instancePath.substring(1).split("/"); + let range; + if (/^\d+$/.test(key[key.length - 1])) { + // We have an array if the key is a number + const value = yamlDoc.getIn(key); + range = (value as Node).range; + } else { + range = map.nodes.get(key.join(".")); + } + if (range) { + start = textDocument.positionAt(range?.[0]); + end = textDocument.positionAt(range?.[1]); + } + message = key.join(".") + " " + e.message; + } else { + // All other schema errors + const key = e.instancePath.substring(1).replace(/\//g, "."); + const range = map.nodes.get(key); + if (range) { + start = textDocument.positionAt(range?.[0]); + end = textDocument.positionAt(range?.[1]); + } + message = key + " " + e.message; } + diagnostics.push({ message: message, range: { start, end } }); }); - - // Shift generated diagnostics by line of model, and indent of 2 - let dslDiagnostics = getDiagnosticsForDsl(yamlDoc.get("model") as string); - dslDiagnostics = dslDiagnostics.map((d) => { - const r = d.range; - r.start.line += position.line; - r.start.character += 2; - r.end.line += position.line; - r.end.character += 2; - return d; - }); - diagnostics.push(...dslDiagnostics); } + } finally { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } - - return diagnostics; - } - - function validateYAML(textDocument: TextDocument): void { - connection.sendDiagnostics({ - uri: textDocument.uri, - diagnostics: [], - }); - - const diagnostics: Diagnostic[] = []; - - diagnostics.push(...validateYamlSyntaxAndModel(textDocument)); - - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } function validateTextDocument(textDocument: TextDocument): void { if (textDocument.languageId === "openfga") { validateDSL(textDocument); } else if (textDocument.languageId === "yaml-store-openfga") { - validateYAML(textDocument); + validateYamlSyntaxAndModel(textDocument); } } @@ -362,40 +337,6 @@ export function startServer(connection: _Connection) { }; }); - function getRangeOfWord(document: TextDocument, position: Position): Range { - const text = document.getText(); - - let pointerStart = document.offsetAt(position); - let pointerEnd = document.offsetAt(position); - - while (text.charAt(pointerStart).match(/\w/)) { - pointerStart--; - } - - while (text.charAt(pointerEnd).match(/\w/)) { - pointerEnd++; - } - - let start = document.positionAt(pointerStart + 1); - let end = document.positionAt(pointerEnd); - - if ( - document.getText({ start, end }) === "but" && - // If we've hovered "but", track forward and check if there is a matching "not" - document.getText({ start, end: document.positionAt(pointerEnd + 4) }) === "but not" - ) { - end = document.positionAt(pointerEnd + 4); - } else if ( - document.getText({ start, end }) === "not" && - // If we've hovered "not", track backward and check if there is a matching "but" - document.getText({ start: document.positionAt(pointerStart - 3), end }) === "but not" - ) { - start = document.positionAt(pointerStart - 3); - } - - return { start, end }; - } - // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection);