mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-25 01:14:23 +01:00
Merge branch 'main' into feature/BRU-18--inherit-auth-mode
This commit is contained in:
commit
389a383b99
208
package-lock.json
generated
208
package-lock.json
generated
@ -47,7 +47,6 @@
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
||||
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
@ -794,7 +793,6 @@
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
|
||||
"integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@ -803,7 +801,6 @@
|
||||
"version": "7.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz",
|
||||
"integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.23.5",
|
||||
@ -870,7 +867,6 @@
|
||||
"version": "7.23.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
|
||||
"integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.23.5",
|
||||
"@babel/helper-validator-option": "^7.23.5",
|
||||
@ -886,7 +882,6 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
@ -894,8 +889,7 @@
|
||||
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.23.10",
|
||||
@ -1011,7 +1005,6 @@
|
||||
"version": "7.23.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
|
||||
"integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-module-imports": "^7.22.15",
|
||||
@ -1084,7 +1077,6 @@
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
|
||||
"integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
@ -1135,7 +1127,6 @@
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
|
||||
"integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@ -1158,7 +1149,6 @@
|
||||
"version": "7.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz",
|
||||
"integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.23.9",
|
||||
"@babel/traverse": "^7.23.9",
|
||||
@ -4529,6 +4519,23 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@n8n/vm2": {
|
||||
"version": "3.9.23",
|
||||
"resolved": "https://registry.npmjs.org/@n8n/vm2/-/vm2-3.9.23.tgz",
|
||||
"integrity": "sha512-yu+It+L89uljQsCJ2e9cQaXzoXJe9bU69QQIoWUOcUw0u5Zon37DuB7bdNNsjKS1ZdFD+fBWCQpq/FkqHsSjXQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.7.0",
|
||||
"acorn-walk": "^8.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"vm2": "bin/vm2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.10",
|
||||
"pnpm": ">=8.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "12.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.3.tgz",
|
||||
@ -6309,6 +6316,17 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-import-assertions": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
|
||||
@ -6318,6 +6336,14 @@
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
|
||||
@ -7401,7 +7427,6 @@
|
||||
"version": "4.22.3",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz",
|
||||
"integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -7522,6 +7547,11 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
|
||||
},
|
||||
"node_modules/buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
@ -8428,8 +8458,7 @@
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
@ -9438,6 +9467,14 @@
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@ -9691,8 +9728,7 @@
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.667",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.667.tgz",
|
||||
"integrity": "sha512-66L3pLlWhTNVUhnmSA5+qDM3fwnXsM6KAqE36e2w4KN0g6pkEtlT5bs41FQtQwVwKnfhNBXiWRLPs30HSxd7Kw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-66L3pLlWhTNVUhnmSA5+qDM3fwnXsM6KAqE36e2w4KN0g6pkEtlT5bs41FQtQwVwKnfhNBXiWRLPs30HSxd7Kw=="
|
||||
},
|
||||
"node_modules/electron-util": {
|
||||
"version": "0.17.2",
|
||||
@ -10816,7 +10852,6 @@
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@ -13661,6 +13696,41 @@
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/semver": {
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jsprim": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
|
||||
@ -13701,6 +13771,25 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/kew": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
|
||||
@ -13955,12 +14044,47 @@
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"node_modules/lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
|
||||
@ -14694,8 +14818,7 @@
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
|
||||
},
|
||||
"node_modules/node-vault": {
|
||||
"version": "0.10.2",
|
||||
@ -17947,7 +18070,6 @@
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@ -19204,18 +19326,6 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
@ -19727,7 +19837,6 @@
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -20024,25 +20133,6 @@
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vm2/node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vm2/node_modules/acorn-walk": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||
@ -20242,18 +20332,6 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
@ -20986,6 +21064,7 @@
|
||||
"dependencies": {
|
||||
"@usebruno/query": "0.1.0",
|
||||
"ajv": "^8.12.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"atob": "^2.1.2",
|
||||
"axios": "^1.5.1",
|
||||
"btoa": "^1.2.1",
|
||||
@ -21073,6 +21152,7 @@
|
||||
"express-xml-bodyparser": "^0.3.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "^1.4.5-lts.1"
|
||||
}
|
||||
|
@ -80,6 +80,15 @@ const AuthMode = ({ item, collection }) => {
|
||||
>
|
||||
Inherit
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef?.current?.hide();
|
||||
onModeChange('oauth2');
|
||||
}}
|
||||
>
|
||||
OAuth 2.0
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
|
@ -0,0 +1,15 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.single-line-editor-wrapper {
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { inputsConfig } from './inputsConfig';
|
||||
|
||||
const OAuth2AuthorizationCode = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
|
||||
|
||||
const handleRun = async () => {
|
||||
dispatch(sendRequest(item, collection.uid));
|
||||
};
|
||||
|
||||
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
const handleChange = (key, value) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'oauth2',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
grantType: 'authorization_code',
|
||||
...oAuth,
|
||||
[key]: value
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 flex w-full gap-4 flex-col">
|
||||
{inputsConfig.map((input) => {
|
||||
const { key, label } = input;
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-1">
|
||||
<label className="block font-medium">{label}</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={oAuth[key] || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleChange(key, val)}
|
||||
onRun={() => {}}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<button onClick={handleRun} className="submit btn btn-sm btn-secondary w-fit">
|
||||
Get Authorization Code
|
||||
</button>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuth2AuthorizationCode;
|
@ -0,0 +1,28 @@
|
||||
const inputsConfig = [
|
||||
{
|
||||
key: 'callbackUrl',
|
||||
label: 'Callback URL'
|
||||
},
|
||||
{
|
||||
key: 'authorizationUrl',
|
||||
label: 'Auth URL'
|
||||
},
|
||||
{
|
||||
key: 'accessTokenUrl',
|
||||
label: 'Access Token URL'
|
||||
},
|
||||
{
|
||||
key: 'clientId',
|
||||
label: 'Client ID'
|
||||
},
|
||||
{
|
||||
key: 'clientSecret',
|
||||
label: 'Client Secret'
|
||||
},
|
||||
{
|
||||
key: 'scope',
|
||||
label: 'Scope'
|
||||
}
|
||||
];
|
||||
|
||||
export { inputsConfig };
|
@ -0,0 +1,15 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.single-line-editor-wrapper {
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const OAuth2ClientCredentials = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
|
||||
|
||||
const handleRun = () => dispatch(sendRequest(item, collection.uid));
|
||||
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
const handleClientIdChange = (clientId) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'oauth2',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
grantType: 'client_credentials',
|
||||
clientId: clientId,
|
||||
clientSecret: oAuth.clientSecret
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleClientSecretChange = (clientSecret) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'oauth2',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
grantType: 'client_credentials',
|
||||
clientId: oAuth.clientId,
|
||||
clientSecret: clientSecret
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
<label className="block font-medium mb-2">Client Id</label>
|
||||
<div className="single-line-editor-wrapper mb-2">
|
||||
<SingleLineEditor
|
||||
value={oAuth.clientId || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleClientIdChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Client Secret</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={oAuth.clientSecret || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleClientSecretChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuth2ClientCredentials;
|
@ -0,0 +1,54 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
font-size: 0.8125rem;
|
||||
|
||||
.grant-type-mode-selector {
|
||||
padding: 0.5rem 0px;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
|
||||
.dropdown {
|
||||
width: 100%;
|
||||
|
||||
div[data-tippy-root] {
|
||||
width: 100%;
|
||||
}
|
||||
.tippy-box {
|
||||
width: 100%;
|
||||
max-width: none !important;
|
||||
|
||||
.tippy-content: {
|
||||
width: 100%;
|
||||
max-width: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grant-type-label {
|
||||
width: 100%;
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
justify-content: space-between;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 0.2rem 0.6rem !important;
|
||||
}
|
||||
|
||||
.label-item {
|
||||
padding: 0.2rem 0.6rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.caret {
|
||||
color: rgb(140, 140, 140);
|
||||
fill: rgb(140 140 140);
|
||||
}
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,75 @@
|
||||
import React, { useRef, forwardRef } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { IconCaretDown } from '@tabler/icons';
|
||||
import { updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { humanizeGrantType } from 'utils/collections';
|
||||
|
||||
const GrantTypeSelector = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const dropdownTippyRef = useRef();
|
||||
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
|
||||
|
||||
const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
|
||||
|
||||
const Icon = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="flex items-center justify-end grant-type-label select-none">
|
||||
{humanizeGrantType(oAuth?.grantType)} <IconCaretDown className="caret ml-1 mr-1" size={14} strokeWidth={2} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const onGrantTypeChange = (grantType) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'oauth2',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
grantType
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<label className="block font-medium mb-2">Grant Type</label>
|
||||
<div className="inline-flex items-center cursor-pointer grant-type-mode-selector w-full">
|
||||
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
onGrantTypeChange('password');
|
||||
}}
|
||||
>
|
||||
Resource Owner Password Credentials
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
onGrantTypeChange('authorization_code');
|
||||
}}
|
||||
>
|
||||
Authorization Code
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
onGrantTypeChange('client_credentials');
|
||||
}}
|
||||
>
|
||||
Client Credentials
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
export default GrantTypeSelector;
|
@ -0,0 +1,15 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.single-line-editor-wrapper {
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const OAuth2Ropc = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
|
||||
|
||||
const handleRun = () => dispatch(sendRequest(item, collection.uid));
|
||||
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
const handleUsernameChange = (username) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'oauth2',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
grantType: 'password',
|
||||
username: username,
|
||||
password: oAuth.password
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handlePasswordChange = (password) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'oauth2',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
grantType: 'password',
|
||||
username: oAuth.username,
|
||||
password: password
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
<label className="block font-medium mb-2">Username</label>
|
||||
<div className="single-line-editor-wrapper mb-2">
|
||||
<SingleLineEditor
|
||||
value={oAuth.username || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleUsernameChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Password</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={oAuth.password || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handlePasswordChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuth2Ropc;
|
@ -0,0 +1,15 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.single-line-editor-wrapper {
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import GrantTypeSelector from './GrantTypeSelector/index';
|
||||
import OAuth2Ropc from './Ropc/index';
|
||||
import OAuth2AuthorizationCode from './AuthorizationCode/index';
|
||||
import OAuth2ClientCredentials from './ClientCredentials/index';
|
||||
|
||||
const grantTypeComponentMap = (grantType, item, collection) => {
|
||||
switch (grantType) {
|
||||
case 'password':
|
||||
return <OAuth2Ropc item={item} collection={collection} />;
|
||||
break;
|
||||
case 'authorization_code':
|
||||
return <OAuth2AuthorizationCode item={item} collection={collection} />;
|
||||
break;
|
||||
case 'client_credentials':
|
||||
return <OAuth2ClientCredentials item={item} collection={collection} />;
|
||||
break;
|
||||
default:
|
||||
return <div>TBD</div>;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const OAuth2 = ({ item, collection }) => {
|
||||
const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {});
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
<GrantTypeSelector item={item} collection={collection} />
|
||||
{grantTypeComponentMap(oAuth?.grantType, item, collection)}
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default OAuth2;
|
@ -7,6 +7,7 @@ import BasicAuth from './BasicAuth';
|
||||
import DigestAuth from './DigestAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections/index';
|
||||
import OAuth2 from './OAuth2/index';
|
||||
|
||||
const Auth = ({ item, collection }) => {
|
||||
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
|
||||
@ -28,6 +29,9 @@ const Auth = ({ item, collection }) => {
|
||||
case 'digest': {
|
||||
return <DigestAuth collection={collection} item={item} />;
|
||||
}
|
||||
case 'oauth2': {
|
||||
return <OAuth2 collection={collection} item={item} />;
|
||||
}
|
||||
case 'inherit': {
|
||||
return (
|
||||
<div className="flex flex-row w-full mt-2 gap-4">
|
||||
@ -36,7 +40,6 @@ const Auth = ({ item, collection }) => {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -140,12 +140,11 @@ export default function RunnerResults({ collection }) {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-col overflow-y-auto"
|
||||
ref={runnerBodyRef}
|
||||
style={{ height: 'calc(100vh - 12rem)', maxHeight: 'calc(100vh - 12rem)' }}
|
||||
>
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="flex flex-row gap-4">
|
||||
<div
|
||||
className="flex flex-col flex-1 overflow-y-auto h-[calc(100vh_-_12rem)] max-h-[calc(100vh_-_12rem)] w-full"
|
||||
ref={runnerBodyRef}
|
||||
>
|
||||
<div className="pb-2 font-medium test-summary">
|
||||
Total Requests: {items.length}, Passed: {passedRequests.length}, Failed: {failedRequests.length}
|
||||
</div>
|
||||
@ -235,8 +234,8 @@ export default function RunnerResults({ collection }) {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-1" style={{ width: '50%' }}>
|
||||
{selectedItem ? (
|
||||
{selectedItem ? (
|
||||
<div className="flex flex-1 w-[50%]">
|
||||
<div className="flex flex-col w-full overflow-auto">
|
||||
<div className="flex items-center px-3 mb-4 font-medium">
|
||||
<span className="mr-2">{selectedItem.relativePath}</span>
|
||||
@ -251,8 +250,8 @@ export default function RunnerResults({ collection }) {
|
||||
{/* <div className='px-3 mb-4 font-medium'>{selectedItem.relativePath}</div> */}
|
||||
<ResponsePane item={selectedItem} collection={collection} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
@ -402,6 +402,10 @@ export const collectionsSlice = createSlice({
|
||||
item.draft.request.auth.mode = 'digest';
|
||||
item.draft.request.auth.digest = action.payload.content;
|
||||
break;
|
||||
case 'oauth2':
|
||||
item.draft.request.auth.mode = 'oauth2';
|
||||
item.draft.request.auth.oauth2 = action.payload.content;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -509,6 +509,30 @@ export const humanizeRequestAuthMode = (mode) => {
|
||||
label = 'Digest Auth';
|
||||
break;
|
||||
}
|
||||
case 'oauth2': {
|
||||
label = 'OAuth 2.0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return label;
|
||||
};
|
||||
|
||||
export const humanizeGrantType = (mode) => {
|
||||
let label = 'No Auth';
|
||||
switch (mode) {
|
||||
case 'password': {
|
||||
label = 'Resource Owner Password Credentials';
|
||||
break;
|
||||
}
|
||||
case 'authorization_code': {
|
||||
label = 'Authorization Code';
|
||||
break;
|
||||
}
|
||||
case 'client_credentials': {
|
||||
label = 'Client Credentials';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return label;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@usebruno/cli",
|
||||
"version": "1.9.0",
|
||||
"version": "1.9.2",
|
||||
"license": "MIT",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
@ -24,9 +24,11 @@
|
||||
"package.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-providers": "^3.425.0",
|
||||
"@usebruno/common": "0.1.0",
|
||||
"@usebruno/js": "0.10.1",
|
||||
"@usebruno/lang": "0.10.0",
|
||||
"aws4-axios": "^3.3.0",
|
||||
"axios": "^1.5.1",
|
||||
"chai": "^4.3.7",
|
||||
"chalk": "^3.0.0",
|
||||
|
56
packages/bruno-cli/src/runner/awsv4auth-helper.js
Normal file
56
packages/bruno-cli/src/runner/awsv4auth-helper.js
Normal file
@ -0,0 +1,56 @@
|
||||
const { fromIni } = require('@aws-sdk/credential-providers');
|
||||
const { aws4Interceptor } = require('aws4-axios');
|
||||
|
||||
function isStrPresent(str) {
|
||||
return str && str !== '' && str !== 'undefined';
|
||||
}
|
||||
|
||||
async function resolveAwsV4Credentials(request) {
|
||||
const awsv4 = request.awsv4config;
|
||||
if (isStrPresent(awsv4.profileName)) {
|
||||
try {
|
||||
credentialsProvider = fromIni({
|
||||
profile: awsv4.profileName
|
||||
});
|
||||
credentials = await credentialsProvider();
|
||||
awsv4.accessKeyId = credentials.accessKeyId;
|
||||
awsv4.secretAccessKey = credentials.secretAccessKey;
|
||||
awsv4.sessionToken = credentials.sessionToken;
|
||||
} catch {
|
||||
console.error('Failed to fetch credentials from AWS profile.');
|
||||
}
|
||||
}
|
||||
return awsv4;
|
||||
}
|
||||
|
||||
function addAwsV4Interceptor(axiosInstance, request) {
|
||||
if (!request.awsv4config) {
|
||||
console.warn('No Auth Config found!');
|
||||
return;
|
||||
}
|
||||
|
||||
const awsv4 = request.awsv4config;
|
||||
if (!isStrPresent(awsv4.accessKeyId) || !isStrPresent(awsv4.secretAccessKey)) {
|
||||
console.warn('Required Auth Fields are not present');
|
||||
return;
|
||||
}
|
||||
|
||||
const interceptor = aws4Interceptor({
|
||||
options: {
|
||||
region: awsv4.region,
|
||||
service: awsv4.service
|
||||
},
|
||||
credentials: {
|
||||
accessKeyId: awsv4.accessKeyId,
|
||||
secretAccessKey: awsv4.secretAccessKey,
|
||||
sessionToken: awsv4.sessionToken
|
||||
}
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.request.use(interceptor);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addAwsV4Interceptor,
|
||||
resolveAwsV4Credentials
|
||||
};
|
@ -57,6 +57,17 @@ const prepareRequest = (request, collectionRoot) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (request.auth.mode === 'awsv4') {
|
||||
axiosRequest.awsv4config = {
|
||||
accessKeyId: get(request, 'auth.awsv4.accessKeyId'),
|
||||
secretAccessKey: get(request, 'auth.awsv4.secretAccessKey'),
|
||||
sessionToken: get(request, 'auth.awsv4.sessionToken'),
|
||||
service: get(request, 'auth.awsv4.service'),
|
||||
region: get(request, 'auth.awsv4.region'),
|
||||
profileName: get(request, 'auth.awsv4.profileName')
|
||||
};
|
||||
}
|
||||
|
||||
if (request.auth.mode === 'bearer') {
|
||||
axiosRequest.headers['authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ const https = require('https');
|
||||
const { HttpProxyAgent } = require('http-proxy-agent');
|
||||
const { SocksProxyAgent } = require('socks-proxy-agent');
|
||||
const { makeAxiosInstance } = require('../utils/axios-instance');
|
||||
const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-helper');
|
||||
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../utils/proxy-util');
|
||||
|
||||
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;
|
||||
@ -190,6 +191,24 @@ const runSingleRequest = async function (
|
||||
// run request
|
||||
const axiosInstance = makeAxiosInstance();
|
||||
|
||||
if (request.awsv4config) {
|
||||
// todo: make this happen in prepare-request.js
|
||||
// interpolate the aws v4 config
|
||||
request.awsv4config.accessKeyId = interpolateString(request.awsv4config.accessKeyId, interpolationOptions);
|
||||
request.awsv4config.secretAccessKey = interpolateString(
|
||||
request.awsv4config.secretAccessKey,
|
||||
interpolationOptions
|
||||
);
|
||||
request.awsv4config.sessionToken = interpolateString(request.awsv4config.sessionToken, interpolationOptions);
|
||||
request.awsv4config.service = interpolateString(request.awsv4config.service, interpolationOptions);
|
||||
request.awsv4config.region = interpolateString(request.awsv4config.region, interpolationOptions);
|
||||
request.awsv4config.profileName = interpolateString(request.awsv4config.profileName, interpolationOptions);
|
||||
|
||||
request.awsv4config = await resolveAwsV4Credentials(request);
|
||||
addAwsV4Interceptor(axiosInstance, request);
|
||||
delete request.awsv4config;
|
||||
}
|
||||
|
||||
/** @type {import('axios').AxiosResponse} */
|
||||
response = await axiosInstance(request);
|
||||
|
||||
|
@ -21,7 +21,8 @@ const contentSecurityPolicy = [
|
||||
"script-src * 'unsafe-inline' 'unsafe-eval'",
|
||||
"connect-src * 'unsafe-inline'",
|
||||
"font-src 'self' https:",
|
||||
"form-action 'none'",
|
||||
// this has been commented out to make oauth2 work
|
||||
// "form-action 'none'",
|
||||
"img-src 'self' blob: data: https:",
|
||||
"style-src 'self' 'unsafe-inline' https:"
|
||||
];
|
||||
|
@ -0,0 +1,61 @@
|
||||
const { BrowserWindow } = require('electron');
|
||||
|
||||
const authorizeUserInWindow = ({ authorizeUrl, callbackUrl }) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let finalUrl = null;
|
||||
|
||||
const window = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: false
|
||||
},
|
||||
show: false
|
||||
});
|
||||
window.on('ready-to-show', window.show.bind(window));
|
||||
|
||||
function onWindowRedirect(url) {
|
||||
// check if the url contains an authorization code
|
||||
if (url.match(/(code=).*/)) {
|
||||
finalUrl = url;
|
||||
if (url && finalUrl.includes(callbackUrl)) {
|
||||
window.close();
|
||||
} else {
|
||||
reject(new Error('Invalid Callback Url'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.on('close', () => {
|
||||
if (finalUrl) {
|
||||
try {
|
||||
const callbackUrlWithCode = new URL(finalUrl);
|
||||
const authorizationCode = callbackUrlWithCode.searchParams.get('code');
|
||||
|
||||
return resolve(authorizationCode);
|
||||
} catch (error) {
|
||||
return reject(error);
|
||||
}
|
||||
} else {
|
||||
return reject(new Error('Authorization window closed'));
|
||||
}
|
||||
});
|
||||
|
||||
// wait for the window to navigate to the callback url
|
||||
const didNavigateListener = (_, url) => {
|
||||
onWindowRedirect(url);
|
||||
};
|
||||
window.webContents.on('did-navigate', didNavigateListener);
|
||||
const willRedirectListener = (_, authorizeUrl) => {
|
||||
onWindowRedirect(authorizeUrl);
|
||||
};
|
||||
window.webContents.on('will-redirect', willRedirectListener);
|
||||
|
||||
try {
|
||||
await window.loadURL(authorizeUrl);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = { authorizeUserInWindow };
|
@ -9,7 +9,7 @@ const Mustache = require('mustache');
|
||||
const contentDispositionParser = require('content-disposition');
|
||||
const mime = require('mime-types');
|
||||
const { ipcMain } = require('electron');
|
||||
const { isUndefined, isNull, each, get, compact } = require('lodash');
|
||||
const { isUndefined, isNull, each, get, compact, cloneDeep } = require('lodash');
|
||||
const { VarsRuntime, AssertRuntime, ScriptRuntime, TestRuntime } = require('@usebruno/js');
|
||||
const prepareRequest = require('./prepare-request');
|
||||
const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request');
|
||||
@ -29,6 +29,7 @@ const { addDigestInterceptor } = require('./digestauth-helper');
|
||||
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-util');
|
||||
const { chooseFileToSave, writeBinaryFile } = require('../../utils/filesystem');
|
||||
const { getCookieStringForUrl, addCookieToJar, getDomainsWithCookies } = require('../../utils/cookies');
|
||||
const { resolveOAuth2AuthorizationCodecessToken } = require('./oauth2-authorization-code-helper');
|
||||
|
||||
// override the default escape function to prevent escaping
|
||||
Mustache.escape = function (value) {
|
||||
@ -189,6 +190,16 @@ const configureRequest = async (
|
||||
|
||||
const axiosInstance = makeAxiosInstance();
|
||||
|
||||
if (request.oauth2) {
|
||||
if (request?.oauth2?.grantType == 'authorization_code') {
|
||||
let requestCopy = cloneDeep(request);
|
||||
interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
|
||||
const { data, url } = await resolveOAuth2AuthorizationCodecessToken(requestCopy);
|
||||
request.data = data;
|
||||
request.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.awsv4config) {
|
||||
request.awsv4config = await resolveAwsV4Credentials(request);
|
||||
addAwsV4Interceptor(axiosInstance, request);
|
||||
@ -484,7 +495,6 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
setCookieHeaders = Array.isArray(response.headers['set-cookie'])
|
||||
? response.headers['set-cookie']
|
||||
: [response.headers['set-cookie']];
|
||||
|
||||
for (let setCookieHeader of setCookieHeaders) {
|
||||
if (typeof setCookieHeader === 'string' && setCookieHeader.length) {
|
||||
addCookieToJar(setCookieHeader, request.url);
|
||||
@ -495,6 +505,7 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
|
||||
// send domain cookies to renderer
|
||||
const domainsWithCookies = await getDomainsWithCookies();
|
||||
|
||||
mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookies)));
|
||||
|
||||
await runPostResponse(
|
||||
|
@ -98,17 +98,53 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces
|
||||
}
|
||||
|
||||
// todo: we have things happening in two places w.r.t basic auth
|
||||
// need to refactor this in the future
|
||||
// need to refactor this in the future
|
||||
// the request.auth (basic auth) object gets set inside the prepare-request.js file
|
||||
if (request.auth) {
|
||||
const username = _interpolate(request.auth.username) || '';
|
||||
const password = _interpolate(request.auth.password) || '';
|
||||
|
||||
// use auth header based approach and delete the request.auth object
|
||||
request.headers['authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
|
||||
delete request.auth;
|
||||
}
|
||||
|
||||
if (request?.oauth2?.grantType) {
|
||||
switch (request.oauth2.grantType) {
|
||||
case 'password':
|
||||
let username = _interpolate(request.oauth2.username) || '';
|
||||
let password = _interpolate(request.oauth2.password) || '';
|
||||
request.oauth2.username = username;
|
||||
request.oauth2.password = password;
|
||||
request.data = {
|
||||
grant_type: 'password',
|
||||
username,
|
||||
password
|
||||
};
|
||||
break;
|
||||
case 'authorization_code':
|
||||
request.oauth2.callbackUrl = _interpolate(request.oauth2.callbackUrl) || '';
|
||||
request.oauth2.authorizationUrl = _interpolate(request.oauth2.authorizationUrl) || '';
|
||||
request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
|
||||
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
|
||||
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
|
||||
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
|
||||
break;
|
||||
case 'client_credentials':
|
||||
let clientId = _interpolate(request.oauth2.clientId) || '';
|
||||
let clientSecret = _interpolate(request.oauth2.clientSecret) || '';
|
||||
request.oauth2.clientId = clientId;
|
||||
request.oauth2.clientSecret = clientSecret;
|
||||
request.data = {
|
||||
grant_type: 'client_credentials',
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// interpolate vars for aws sigv4 auth
|
||||
if (request.awsv4config) {
|
||||
request.awsv4config.accessKeyId = _interpolate(request.awsv4config.accessKeyId) || '';
|
||||
|
@ -0,0 +1,41 @@
|
||||
const { get, cloneDeep } = require('lodash');
|
||||
const { authorizeUserInWindow } = require('./authorize-user-in-window');
|
||||
|
||||
const resolveOAuth2AuthorizationCodecessToken = async (request) => {
|
||||
let requestCopy = cloneDeep(request);
|
||||
const authorization_code = await getOAuth2AuthorizationCode(requestCopy);
|
||||
const oAuth = get(requestCopy, 'oauth2', {});
|
||||
const { clientId, clientSecret, callbackUrl, scope } = oAuth;
|
||||
const data = {
|
||||
grant_type: 'authorization_code',
|
||||
code: authorization_code,
|
||||
redirect_uri: callbackUrl,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
scope: scope
|
||||
};
|
||||
const url = requestCopy?.oauth2?.accessTokenUrl;
|
||||
return {
|
||||
data,
|
||||
url
|
||||
};
|
||||
};
|
||||
|
||||
const getOAuth2AuthorizationCode = (request) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const { oauth2 } = request;
|
||||
const { callbackUrl, clientId, authorizationUrl, scope } = oauth2;
|
||||
const authorizationUrlWithQueryParams = `${authorizationUrl}?client_id=${clientId}&redirect_uri=${callbackUrl}&response_type=code&scope=${scope}`;
|
||||
try {
|
||||
const code = await authorizeUserInWindow({ authorizeUrl: authorizationUrlWithQueryParams, callbackUrl });
|
||||
resolve(code);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
resolveOAuth2AuthorizationCodecessToken,
|
||||
getOAuth2AuthorizationCode
|
||||
};
|
@ -62,6 +62,36 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
password: get(collectionAuth, 'digest.password')
|
||||
};
|
||||
break;
|
||||
case 'oauth2':
|
||||
const grantType = get(collectionAuth, 'auth.oauth2.grantType');
|
||||
switch (grantType) {
|
||||
case 'password':
|
||||
axiosRequest.oauth2 = {
|
||||
grantType: grantType,
|
||||
username: get(collectionAuth, 'auth.oauth2.username'),
|
||||
password: get(collectionAuth, 'auth.oauth2.password')
|
||||
};
|
||||
break;
|
||||
case 'authorization_code':
|
||||
axiosRequest.oauth2 = {
|
||||
grantType: grantType,
|
||||
callbackUrl: get(collectionAuth, 'auth.oauth2.callbackUrl'),
|
||||
authorizationUrl: get(collectionAuth, 'auth.oauth2.authorizationUrl'),
|
||||
accessTokenUrl: get(collectionAuth, 'auth.oauth2.accessTokenUrl'),
|
||||
clientId: get(collectionAuth, 'auth.oauth2.clientId'),
|
||||
clientSecret: get(collectionAuth, 'auth.oauth2.clientSecret'),
|
||||
scope: get(collectionAuth, 'auth.oauth2.scope')
|
||||
};
|
||||
break;
|
||||
case 'client_credentials':
|
||||
axiosRequest.oauth2 = {
|
||||
grantType: grantType,
|
||||
clientId: get(collectionAuth, 'auth.oauth2.clientId'),
|
||||
clientSecret: get(collectionAuth, 'auth.oauth2.clientSecret')
|
||||
};
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +121,37 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
username: get(request, 'auth.digest.username'),
|
||||
password: get(request, 'auth.digest.password')
|
||||
};
|
||||
break;
|
||||
case 'oauth2':
|
||||
const grantType = get(request, 'auth.oauth2.grantType');
|
||||
switch (grantType) {
|
||||
case 'password':
|
||||
axiosRequest.oauth2 = {
|
||||
grantType: grantType,
|
||||
username: get(request, 'auth.oauth2.username'),
|
||||
password: get(request, 'auth.oauth2.password')
|
||||
};
|
||||
break;
|
||||
case 'authorization_code':
|
||||
axiosRequest.oauth2 = {
|
||||
grantType: grantType,
|
||||
callbackUrl: get(request, 'auth.oauth2.callbackUrl'),
|
||||
authorizationUrl: get(request, 'auth.oauth2.authorizationUrl'),
|
||||
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
|
||||
clientId: get(request, 'auth.oauth2.clientId'),
|
||||
clientSecret: get(request, 'auth.oauth2.clientSecret'),
|
||||
scope: get(request, 'auth.oauth2.scope')
|
||||
};
|
||||
break;
|
||||
case 'client_credentials':
|
||||
axiosRequest.oauth2 = {
|
||||
grantType: grantType,
|
||||
clientId: get(request, 'auth.oauth2.clientId'),
|
||||
clientSecret: get(request, 'auth.oauth2.clientSecret')
|
||||
};
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ class EnvironmentSecretsStore {
|
||||
}
|
||||
|
||||
isValidValue(val) {
|
||||
return val && typeof val === 'string' && val.length > 0;
|
||||
return typeof val === 'string' && val.length >= 0;
|
||||
}
|
||||
|
||||
storeEnvSecrets(collectionPathname, environment) {
|
||||
|
@ -48,7 +48,7 @@ function safeStorageDecrypt(str) {
|
||||
}
|
||||
|
||||
function encryptString(str) {
|
||||
if (!str || typeof str !== 'string' || str.length === 0) {
|
||||
if (typeof str !== 'string') {
|
||||
throw new Error('Encrypt failed: invalid string');
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"@usebruno/query": "0.1.0",
|
||||
"ajv": "^8.12.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"atob": "^2.1.2",
|
||||
"axios": "^1.5.1",
|
||||
"btoa": "^1.2.1",
|
||||
@ -28,7 +29,7 @@
|
||||
"moment": "^2.29.4",
|
||||
"nanoid": "3.3.4",
|
||||
"node-fetch": "2.*",
|
||||
"uuid": "^9.0.0",
|
||||
"node-vault": "^0.10.2"
|
||||
"node-vault": "^0.10.2",
|
||||
"uuid": "^9.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ const { cleanJson } = require('../utils');
|
||||
|
||||
// Inbuilt Library Support
|
||||
const ajv = require('ajv');
|
||||
const addFormats = require('ajv-formats');
|
||||
const atob = require('atob');
|
||||
const btoa = require('btoa');
|
||||
const lodash = require('lodash');
|
||||
@ -102,6 +103,7 @@ class ScriptRuntime {
|
||||
zlib,
|
||||
// 3rd party libs
|
||||
ajv,
|
||||
'ajv-formats': addFormats,
|
||||
atob,
|
||||
btoa,
|
||||
lodash,
|
||||
@ -194,6 +196,7 @@ class ScriptRuntime {
|
||||
zlib,
|
||||
// 3rd party libs
|
||||
ajv,
|
||||
'ajv-formats': addFormats,
|
||||
atob,
|
||||
btoa,
|
||||
lodash,
|
||||
|
@ -19,6 +19,7 @@ const { cleanJson } = require('../utils');
|
||||
|
||||
// Inbuilt Library Support
|
||||
const ajv = require('ajv');
|
||||
const addFormats = require('ajv-formats');
|
||||
const atob = require('atob');
|
||||
const btoa = require('btoa');
|
||||
const lodash = require('lodash');
|
||||
@ -120,6 +121,7 @@ class TestRuntime {
|
||||
zlib,
|
||||
// 3rd party libs
|
||||
ajv,
|
||||
'ajv-formats': addFormats,
|
||||
btoa,
|
||||
atob,
|
||||
lodash,
|
||||
|
@ -1,5 +1,6 @@
|
||||
const { describe, it, expect } = require('@jest/globals');
|
||||
const TestRuntime = require('../src/runtime/test-runtime');
|
||||
const ScriptRuntime = require('../src/runtime/script-runtime');
|
||||
|
||||
describe('runtime', () => {
|
||||
describe('test-runtime', () => {
|
||||
@ -49,5 +50,129 @@ describe('runtime', () => {
|
||||
{ description: 'async test', status: 'pass' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('should have ajv and ajv-formats dependencies available', async () => {
|
||||
const testFile = `
|
||||
const Ajv = require('ajv');
|
||||
const addFormats = require("ajv-formats");
|
||||
const ajv = new Ajv();
|
||||
addFormats(ajv);
|
||||
|
||||
const schema = {
|
||||
type: 'string',
|
||||
format: 'date-time'
|
||||
};
|
||||
|
||||
const validate = ajv.compile(schema)
|
||||
|
||||
test('format valid', () => {
|
||||
const valid = validate(new Date().toISOString())
|
||||
expect(valid).to.be.true;
|
||||
})
|
||||
`;
|
||||
|
||||
const runtime = new TestRuntime();
|
||||
const result = await runtime.runTests(
|
||||
testFile,
|
||||
{ ...baseRequest },
|
||||
{ ...baseResponse },
|
||||
{},
|
||||
{},
|
||||
'.',
|
||||
null,
|
||||
process.env
|
||||
);
|
||||
expect(result.results.map((el) => ({ description: el.description, status: el.status }))).toEqual([
|
||||
{ description: 'format valid', status: 'pass' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('script-runtime', () => {
|
||||
describe('run-request-script', () => {
|
||||
const baseRequest = {
|
||||
method: 'GET',
|
||||
url: 'http://localhost:3000/',
|
||||
headers: {},
|
||||
data: undefined
|
||||
};
|
||||
|
||||
it('should have ajv and ajv-formats dependencies available', async () => {
|
||||
const script = `
|
||||
const Ajv = require('ajv');
|
||||
const addFormats = require("ajv-formats");
|
||||
const ajv = new Ajv();
|
||||
addFormats(ajv);
|
||||
|
||||
const schema = {
|
||||
type: 'string',
|
||||
format: 'date-time'
|
||||
};
|
||||
|
||||
const validate = ajv.compile(schema)
|
||||
|
||||
bru.setVar('validation', validate(new Date().toISOString()))
|
||||
`;
|
||||
|
||||
const runtime = new ScriptRuntime();
|
||||
const result = await runtime.runRequestScript(script, { ...baseRequest }, {}, {}, '.', null, process.env);
|
||||
expect(result.collectionVariables.validation).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('run-response-script', () => {
|
||||
const baseRequest = {
|
||||
method: 'GET',
|
||||
url: 'http://localhost:3000/',
|
||||
headers: {},
|
||||
data: undefined
|
||||
};
|
||||
const baseResponse = {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
data: [
|
||||
{
|
||||
id: 1
|
||||
},
|
||||
{
|
||||
id: 2
|
||||
},
|
||||
{
|
||||
id: 3
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
it('should have ajv and ajv-formats dependencies available', async () => {
|
||||
const script = `
|
||||
const Ajv = require('ajv');
|
||||
const addFormats = require("ajv-formats");
|
||||
const ajv = new Ajv();
|
||||
addFormats(ajv);
|
||||
|
||||
const schema = {
|
||||
type: 'string',
|
||||
format: 'date-time'
|
||||
};
|
||||
|
||||
const validate = ajv.compile(schema)
|
||||
|
||||
bru.setVar('validation', validate(new Date().toISOString()))
|
||||
`;
|
||||
|
||||
const runtime = new ScriptRuntime();
|
||||
const result = await runtime.runResponseScript(
|
||||
script,
|
||||
{ ...baseRequest },
|
||||
{ ...baseResponse },
|
||||
{},
|
||||
{},
|
||||
'.',
|
||||
null,
|
||||
process.env
|
||||
);
|
||||
expect(result.collectionVariables.validation).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -23,7 +23,7 @@ const { outdentString } = require('../../v1/src/utils');
|
||||
*/
|
||||
const grammar = ohm.grammar(`Bru {
|
||||
BruFile = (meta | http | query | headers | auths | bodies | varsandassert | script | tests | docs)*
|
||||
auths = authawsv4 | authbasic | authbearer | authdigest
|
||||
auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2
|
||||
bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body
|
||||
bodyforms = bodyformurlencoded | bodymultipart
|
||||
|
||||
@ -80,6 +80,7 @@ const grammar = ohm.grammar(`Bru {
|
||||
authbasic = "auth:basic" dictionary
|
||||
authbearer = "auth:bearer" dictionary
|
||||
authdigest = "auth:digest" dictionary
|
||||
authOAuth2 = "auth:oauth2" dictionary
|
||||
|
||||
body = "body" st* "{" nl* textblock tagend
|
||||
bodyjson = "body:json" st* "{" nl* textblock tagend
|
||||
@ -380,6 +381,46 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
||||
}
|
||||
};
|
||||
},
|
||||
authOAuth2(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
const grantTypeKey = _.find(auth, { name: 'grant_type' });
|
||||
const usernameKey = _.find(auth, { name: 'username' });
|
||||
const passwordKey = _.find(auth, { name: 'password' });
|
||||
const callbackUrlKey = _.find(auth, { name: 'callback_url' });
|
||||
const authorizationUrlKey = _.find(auth, { name: 'authorization_url' });
|
||||
const accessTokenUrlKey = _.find(auth, { name: 'access_token_url' });
|
||||
const clientIdKey = _.find(auth, { name: 'client_id' });
|
||||
const clientSecretKey = _.find(auth, { name: 'client_secret' });
|
||||
const scopeKey = _.find(auth, { name: 'scope' });
|
||||
return {
|
||||
auth: {
|
||||
oauth2:
|
||||
grantTypeKey?.value && grantTypeKey?.value == 'password'
|
||||
? {
|
||||
grantType: grantTypeKey ? grantTypeKey.value : '',
|
||||
username: usernameKey ? usernameKey.value : '',
|
||||
password: passwordKey ? passwordKey.value : ''
|
||||
}
|
||||
: grantTypeKey?.value && grantTypeKey?.value == 'authorization_code'
|
||||
? {
|
||||
grantType: grantTypeKey ? grantTypeKey.value : '',
|
||||
callbackUrl: callbackUrlKey ? callbackUrlKey.value : '',
|
||||
authorizationUrl: authorizationUrlKey ? authorizationUrlKey.value : '',
|
||||
accessTokenUrl: accessTokenUrlKey ? accessTokenUrlKey.value : '',
|
||||
clientId: clientIdKey ? clientIdKey.value : '',
|
||||
clientSecret: clientSecretKey ? clientSecretKey.value : '',
|
||||
scope: scopeKey ? scopeKey.value : ''
|
||||
}
|
||||
: grantTypeKey?.value && grantTypeKey?.value == 'client_credentials'
|
||||
? {
|
||||
grantType: grantTypeKey ? grantTypeKey.value : '',
|
||||
clientId: clientIdKey ? clientIdKey.value : '',
|
||||
clientSecret: clientSecretKey ? clientSecretKey.value : ''
|
||||
}
|
||||
: {}
|
||||
}
|
||||
};
|
||||
},
|
||||
bodyformurlencoded(_1, dictionary) {
|
||||
return {
|
||||
body: {
|
||||
|
@ -126,6 +126,42 @@ ${indentString(`password: ${auth.digest.password}`)}
|
||||
`;
|
||||
}
|
||||
|
||||
if (auth && auth.oauth2) {
|
||||
switch (auth?.oauth2?.grantType) {
|
||||
case 'password':
|
||||
bru += `auth:oauth2 {
|
||||
${indentString(`grant_type: password`)}
|
||||
${indentString(`username: ${auth.oauth2.username}`)}
|
||||
${indentString(`password: ${auth.oauth2.password}`)}
|
||||
}
|
||||
|
||||
`;
|
||||
break;
|
||||
case 'authorization_code':
|
||||
bru += `auth:oauth2 {
|
||||
${indentString(`grant_type: authorization_code`)}
|
||||
${indentString(`callback_url: ${auth.oauth2.callbackUrl}`)}
|
||||
${indentString(`authorization_url: ${auth.oauth2.authorizationUrl}`)}
|
||||
${indentString(`access_token_url: ${auth.oauth2.accessTokenUrl}`)}
|
||||
${indentString(`client_id: ${auth.oauth2.clientId}`)}
|
||||
${indentString(`client_secret: ${auth.oauth2.clientSecret}`)}
|
||||
${indentString(`scope: ${auth.oauth2.scope}`)}
|
||||
}
|
||||
|
||||
`;
|
||||
break;
|
||||
case 'client_credentials':
|
||||
bru += `auth:oauth2 {
|
||||
${indentString(`grant_type: client_credentials`)}
|
||||
${indentString(`client_id: ${auth.oauth2.clientId}`)}
|
||||
${indentString(`client_secret: ${auth.oauth2.clientSecret}`)}
|
||||
}
|
||||
|
||||
`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (body && body.json && body.json.length) {
|
||||
bru += `body:json {
|
||||
${indentString(body.json)}
|
||||
|
@ -45,6 +45,15 @@ auth:digest {
|
||||
password: secret
|
||||
}
|
||||
|
||||
auth:oauth2 {
|
||||
grantType: authorization_code
|
||||
client_id: client_id_1
|
||||
client_secret: client_secret_1
|
||||
auth_url: http://localhost:8080/api/auth/oauth2/ac/authorize
|
||||
callback_url: http://localhost:8080/api/auth/oauth2/ac/callback
|
||||
access_token_url: http://localhost:8080/api/auth/oauth2/ac/token
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"hello": "world"
|
||||
|
@ -63,6 +63,14 @@
|
||||
"digest": {
|
||||
"username": "john",
|
||||
"password": "secret"
|
||||
},
|
||||
"oauth2": {
|
||||
"grantType": "authorization_code",
|
||||
"client_id": "client_id_1",
|
||||
"client_secret": "client_secret_1",
|
||||
"auth_url": "http://localhost:8080/api/auth/oauth2/ac/authorize",
|
||||
"callback_url": "http://localhost:8080/api/auth/oauth2/ac/callback",
|
||||
"access_token_url": "http://localhost:8080/api/auth/oauth2/ac/token"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
|
@ -119,12 +119,61 @@ const authDigestSchema = Yup.object({
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
||||
const oauth2Schema = Yup.object({
|
||||
grantType: Yup.string()
|
||||
.oneOf(['client_credentials', 'password', 'authorization_code'])
|
||||
.required('grantType is required'),
|
||||
username: Yup.string().when('grantType', {
|
||||
is: (val) => ['client_credentials', 'password'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
}),
|
||||
password: Yup.string().when('grantType', {
|
||||
is: (val) => ['client_credentials', 'password'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
}),
|
||||
callbackUrl: Yup.string().when('grantType', {
|
||||
is: (val) => ['authorization_code'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
}),
|
||||
authorizationUrl: Yup.string().when('grantType', {
|
||||
is: (val) => ['authorization_code'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
}),
|
||||
accessTokenUrl: Yup.string().when('grantType', {
|
||||
is: (val) => ['authorization_code'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
}),
|
||||
clientId: Yup.string().when('grantType', {
|
||||
is: (val) => ['authorization_code', 'client_credentials'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
}),
|
||||
clientSecret: Yup.string().when('grantType', {
|
||||
is: (val) => ['authorization_code', 'client_credentials'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
}),
|
||||
scope: Yup.string().when('grantType', {
|
||||
is: (val) => ['authorization_code'].includes(val),
|
||||
then: Yup.string().nullable(),
|
||||
otherwise: Yup.string().nullable().strip()
|
||||
})
|
||||
})
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
||||
const authSchema = Yup.object({
|
||||
mode: Yup.string().oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest']).required('mode is required'),
|
||||
mode: Yup.string().oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'oauth2']).required('mode is required'),
|
||||
awsv4: authAwsV4Schema.nullable(),
|
||||
basic: authBasicSchema.nullable(),
|
||||
bearer: authBearerSchema.nullable(),
|
||||
digest: authDigestSchema.nullable()
|
||||
digest: authDigestSchema.nullable(),
|
||||
oauth2: oauth2Schema.nullable()
|
||||
})
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
@ -15,7 +15,7 @@
|
||||
"bypassProxy": ""
|
||||
},
|
||||
"scripts": {
|
||||
"moduleWhitelist": ["crypto"],
|
||||
"moduleWhitelist": ["crypto", "buffer"],
|
||||
"filesystemAccess": {
|
||||
"allow": true
|
||||
}
|
||||
|
@ -1,8 +1,28 @@
|
||||
vars {
|
||||
host: http://localhost:80
|
||||
host: http://localhost:8080
|
||||
bearer_auth_token: your_secret_token
|
||||
basic_auth_password: della
|
||||
env.var1: envVar1
|
||||
env-var2: envVar2
|
||||
bark: {{process.env.PROC_ENV_VAR}}
|
||||
client_id: client_id_1
|
||||
client_secret: client_secret_1
|
||||
auth_url: http://localhost:8080/api/auth/oauth2/ac/authorize
|
||||
callback_url: http://localhost:8080/api/auth/oauth2/ac/callback
|
||||
access_token_url: http://localhost:8080/api/auth/oauth2/ac/token
|
||||
ropc_username: foo
|
||||
ropc_password: bar
|
||||
github_authorize_url: https://github.com/login/oauth/authorize
|
||||
github_access_token_url: https://github.com/login/oauth/access_token
|
||||
google_auth_url: https://accounts.google.com/o/oauth2/auth
|
||||
google_access_token_url: https://accounts.google.com/o/oauth2/token
|
||||
google_scope: https://www.googleapis.com/auth/userinfo.email
|
||||
}
|
||||
vars:secret [
|
||||
github_client_secret,
|
||||
github_client_id,
|
||||
google_client_id,
|
||||
google_client_secret,
|
||||
github_authorization_code,
|
||||
ropc_access_token,
|
||||
cc_access_token,
|
||||
ac_access_token,
|
||||
github_access_token
|
||||
]
|
||||
|
151
packages/bruno-tests/collection/package-lock.json
generated
151
packages/bruno-tests/collection/package-lock.json
generated
@ -8,7 +8,9 @@
|
||||
"name": "@usebruno/test-collection",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^8.4.0"
|
||||
"@faker-js/faker": "^8.4.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lru-map-cache": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@faker-js/faker": {
|
||||
@ -25,6 +27,153 @@
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
"npm": ">=6.14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
|
||||
"dependencies": {
|
||||
"jwa": "^1.4.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-map-cache": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-map-cache/-/lru-map-cache-0.1.0.tgz",
|
||||
"integrity": "sha512-r1lasvJbg3lrTS37W5h4Ugy9miaWluYqviZGbfH9A6AbjxSDJCtPNqtGr5MRl/RG/EfYrwe07DC4zQEBnY2q4w=="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
"name": "@usebruno/test-collection",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^8.4.0"
|
||||
"@faker-js/faker": "^8.4.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lru-map-cache": "^0.1.0"
|
||||
}
|
||||
}
|
||||
|
1
packages/bruno-tests/collection_oauth2/.env
Normal file
1
packages/bruno-tests/collection_oauth2/.env
Normal file
@ -0,0 +1 @@
|
||||
PROC_ENV_VAR=woof
|
1
packages/bruno-tests/collection_oauth2/.gitignore
vendored
Normal file
1
packages/bruno-tests/collection_oauth2/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
!.env
|
1
packages/bruno-tests/collection_oauth2/.nvmrc
Normal file
1
packages/bruno-tests/collection_oauth2/.nvmrc
Normal file
@ -0,0 +1 @@
|
||||
v18
|
@ -0,0 +1,25 @@
|
||||
meta {
|
||||
name: github token with authorize
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: github.com
|
||||
body: none
|
||||
auth: oauth2
|
||||
}
|
||||
|
||||
auth:oauth2 {
|
||||
grant_type: authorization_code
|
||||
callback_url: {{callback_url}}
|
||||
authorization_url: {{github_authorize_url}}
|
||||
access_token_url: {{github_access_token_url}}
|
||||
client_id: {{github_client_id}}
|
||||
client_secret: {{github_client_secret}}
|
||||
scope: repo,gist
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
bru.setEnvVar('github_access_token',res.body.split('access_token=')[1]?.split('&scope')[0]);
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
meta {
|
||||
name: google token with authorize
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
post {
|
||||
url:
|
||||
body: none
|
||||
auth: oauth2
|
||||
}
|
||||
|
||||
auth:oauth2 {
|
||||
grant_type: authorization_code
|
||||
callback_url: {{callback_url}}
|
||||
authorization_url: {{google_auth_url}}
|
||||
access_token_url: {{google_access_token_url}}
|
||||
client_id: {{google_client_id}}
|
||||
client_secret: {{google_client_secret}}
|
||||
scope: {{google_scope}}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
bru.setEnvVar('ac_access_token', res.body.access_token);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
meta {
|
||||
name: resource
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{host}}/api/auth/oauth2/ac/resource?token={{ac_access_token}}
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
query {
|
||||
token: {{ac_access_token}}
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token:
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"code": "eb30dbf783b65bec4539ee1dcb068606",
|
||||
"client_id": "{{client_id}}",
|
||||
"client_secret": "{{client_secret}}"
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
meta {
|
||||
name: token with authorize
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
post {
|
||||
url:
|
||||
body: none
|
||||
auth: oauth2
|
||||
}
|
||||
|
||||
auth:oauth2 {
|
||||
grant_type: authorization_code
|
||||
callback_url: {{callback_url}}
|
||||
authorization_url: {{auth_url}}
|
||||
access_token_url: {{access_token_url}}
|
||||
client_id: {{client_id}}
|
||||
client_secret: {{client_secret}}
|
||||
scope:
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
bru.setEnvVar('ac_access_token', res.body.access_token);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
meta {
|
||||
name: resource
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{host}}/api/auth/oauth2/cc/resource?token={{cc_access_token}}
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
query {
|
||||
token: {{cc_access_token}}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
meta {
|
||||
name: token
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{host}}/api/auth/oauth2/cc/token
|
||||
body: none
|
||||
auth: oauth2
|
||||
}
|
||||
|
||||
auth:oauth2 {
|
||||
grant_type: client_credentials
|
||||
client_id: {{client_id}}
|
||||
client_secret: {{client_secret}}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
bru.setEnvVar('cc_access_token', res.body.access_token);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
meta {
|
||||
name: resource
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{host}}/api/auth/oauth2/ropc/resource
|
||||
body: none
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{ropc_access_token}}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
meta {
|
||||
name: token
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{host}}/api/auth/oauth2/ropc/token
|
||||
body: none
|
||||
auth: oauth2
|
||||
}
|
||||
|
||||
auth:oauth2 {
|
||||
grant_type: password
|
||||
username: {{ropc_username}}
|
||||
password: {{ropc_password}}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
bru.setEnvVar('ropc_access_token', res.body.access_token);
|
||||
}
|
31
packages/bruno-tests/collection_oauth2/bruno.json
Normal file
31
packages/bruno-tests/collection_oauth2/bruno.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "bruno-testbench",
|
||||
"type": "collection",
|
||||
"proxy": {
|
||||
"enabled": false,
|
||||
"protocol": "http",
|
||||
"hostname": "{{proxyHostname}}",
|
||||
"port": 4000,
|
||||
"auth": {
|
||||
"enabled": false,
|
||||
"username": "anoop",
|
||||
"password": "password"
|
||||
},
|
||||
"bypassProxy": ""
|
||||
},
|
||||
"scripts": {
|
||||
"moduleWhitelist": ["crypto"],
|
||||
"filesystemAccess": {
|
||||
"allow": true
|
||||
}
|
||||
},
|
||||
"clientCertificates": {
|
||||
"enabled": true,
|
||||
"certs": []
|
||||
},
|
||||
"presets": {
|
||||
"requestType": "http",
|
||||
"requestUrl": "http://localhost:6000"
|
||||
}
|
||||
}
|
22
packages/bruno-tests/collection_oauth2/collection.bru
Normal file
22
packages/bruno-tests/collection_oauth2/collection.bru
Normal file
@ -0,0 +1,22 @@
|
||||
headers {
|
||||
check: again
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: none
|
||||
}
|
||||
|
||||
auth:basic {
|
||||
username: bruno
|
||||
password: {{basicAuthPassword}}
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{bearerAuthToken}}
|
||||
}
|
||||
|
||||
docs {
|
||||
# bruno-testbench 🐶
|
||||
|
||||
This is a test collection that I am using to test various functionalities around bruno
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
vars {
|
||||
host: http://localhost:8080
|
||||
bearer_auth_token: your_secret_token
|
||||
basic_auth_password: della
|
||||
client_id: client_id_1
|
||||
client_secret: client_secret_1
|
||||
auth_url: http://localhost:8080/api/auth/oauth2/ac/authorize
|
||||
callback_url: http://localhost:8080/api/auth/oauth2/ac/callback
|
||||
access_token_url: http://localhost:8080/api/auth/oauth2/ac/token
|
||||
ropc_username: foo
|
||||
ropc_password: bar
|
||||
github_authorize_url: https://github.com/login/oauth/authorize
|
||||
github_access_token_url: https://github.com/login/oauth/access_token
|
||||
google_auth_url: https://accounts.google.com/o/oauth2/auth
|
||||
google_access_token_url: https://accounts.google.com/o/oauth2/token
|
||||
google_scope: https://www.googleapis.com/auth/userinfo.email
|
||||
}
|
||||
vars:secret [
|
||||
github_client_secret,
|
||||
github_client_id,
|
||||
google_client_id,
|
||||
google_client_secret,
|
||||
github_authorization_code,
|
||||
github_access_token,
|
||||
ac_access_token
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
vars {
|
||||
host: https://testbench-sanity.usebruno.com
|
||||
bearer_auth_token: your_secret_token
|
||||
basic_auth_password: della
|
||||
env.var1: envVar1
|
||||
env-var2: envVar2
|
||||
bark: {{process.env.PROC_ENV_VAR}}
|
||||
}
|
3
packages/bruno-tests/collection_oauth2/file.json
Normal file
3
packages/bruno-tests/collection_oauth2/file.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"hello": "bruno"
|
||||
}
|
30
packages/bruno-tests/collection_oauth2/package-lock.json
generated
Normal file
30
packages/bruno-tests/collection_oauth2/package-lock.json
generated
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@usebruno/test-collection",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@usebruno/test-collection",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^8.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@faker-js/faker": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.0.tgz",
|
||||
"integrity": "sha512-htW87352wzUCdX1jyUQocUcmAaFqcR/w082EC8iP/gtkF0K+aKcBp0hR5Arb7dzR8tQ1TrhE9DNa5EbJELm84w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fakerjs"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
"npm": ">=6.14.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
packages/bruno-tests/collection_oauth2/package.json
Normal file
7
packages/bruno-tests/collection_oauth2/package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "@usebruno/test-collection",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^8.4.0"
|
||||
}
|
||||
}
|
3
packages/bruno-tests/collection_oauth2/readme.md
Normal file
3
packages/bruno-tests/collection_oauth2/readme.md
Normal file
@ -0,0 +1,3 @@
|
||||
# bruno-tests collection
|
||||
|
||||
API Collection to run sanity tests on Bruno CLI.
|
@ -27,6 +27,7 @@
|
||||
"express-xml-bodyparser": "^0.3.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "^1.4.5-lts.1"
|
||||
}
|
||||
|
@ -4,7 +4,13 @@ const router = express.Router();
|
||||
const authBearer = require('./bearer');
|
||||
const authBasic = require('./basic');
|
||||
const authCookie = require('./cookie');
|
||||
const authOAuth2Ropc = require('./oauth2/ropc');
|
||||
const authOAuth2AuthorizationCode = require('./oauth2/ac');
|
||||
const authOAuth2Cc = require('./oauth2/cc');
|
||||
|
||||
router.use('/oauth2/ropc', authOAuth2Ropc);
|
||||
router.use('/oauth2/ac', authOAuth2AuthorizationCode);
|
||||
router.use('/oauth2/cc', authOAuth2Cc);
|
||||
router.use('/bearer', authBearer);
|
||||
router.use('/basic', authBasic);
|
||||
router.use('/cookie', authCookie);
|
||||
|
141
packages/bruno-tests/src/auth/oauth2/ac.js
Normal file
141
packages/bruno-tests/src/auth/oauth2/ac.js
Normal file
@ -0,0 +1,141 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const crypto = require('crypto');
|
||||
const clients = [
|
||||
{
|
||||
client_id: 'client_id_1',
|
||||
client_secret: 'client_secret_1',
|
||||
redirect_uri: 'http://localhost:3001/callback'
|
||||
}
|
||||
];
|
||||
|
||||
const authCodes = [];
|
||||
|
||||
const tokens = [];
|
||||
|
||||
function generateUniqueString() {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
router.get('/authorize', (req, res) => {
|
||||
const { response_type, client_id, redirect_uri } = req.query;
|
||||
if (response_type !== 'code') {
|
||||
return res.status(401).json({ error: 'Invalid Response type, expected "code"' });
|
||||
}
|
||||
|
||||
const client = clients.find((c) => c.client_id === client_id);
|
||||
|
||||
if (!client) {
|
||||
return res.status(401).json({ error: 'Invalid client' });
|
||||
}
|
||||
|
||||
if (!redirect_uri) {
|
||||
return res.status(401).json({ error: 'Invalid redirect URI' });
|
||||
}
|
||||
|
||||
const authorization_code = generateUniqueString();
|
||||
authCodes.push({
|
||||
authCode: authorization_code,
|
||||
client_id,
|
||||
redirect_uri
|
||||
});
|
||||
|
||||
const redirectUrl = `${redirect_uri}?code=${authorization_code}`;
|
||||
|
||||
const _res = `
|
||||
<html>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", (event) => {
|
||||
const buttonElement = document.getElementById('authorize');
|
||||
buttonElement.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
buttonElement.innerText = 'redirecting...';
|
||||
try {
|
||||
const url = new URL("${redirectUrl}");
|
||||
window.location.href = url;
|
||||
}
|
||||
catch(err) {
|
||||
buttonElement.innerText = 'Invalid Redirect URL';
|
||||
console.log('Invalid Redirect URL')
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<body>
|
||||
<button id='authorize'>Authorize</button>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
res.send(_res);
|
||||
});
|
||||
|
||||
// Handle the authorization callback
|
||||
router.get('/callback', (req, res) => {
|
||||
const { code } = req.query;
|
||||
|
||||
// Check if the authCode is valid.
|
||||
const storedAuthCode = authCodes.find((t) => t.authCode === code);
|
||||
|
||||
if (!storedAuthCode) {
|
||||
return res.status(401).json({ error: 'Invalid Authorization Code' });
|
||||
}
|
||||
|
||||
return res.json({ message: 'Authorization successful', storedAuthCode });
|
||||
});
|
||||
|
||||
router.post('/token', (req, res) => {
|
||||
let grant_type, code, redirect_uri, client_id, client_secret;
|
||||
if (req?.body?.grant_type) {
|
||||
grant_type = req?.body?.grant_type;
|
||||
code = req?.body?.code;
|
||||
redirect_uri = req?.body?.redirect_uri;
|
||||
client_id = req?.body?.client_id;
|
||||
client_secret = req?.body?.client_secret;
|
||||
}
|
||||
if (req?.headers?.grant_type) {
|
||||
grant_type = req?.headers?.grant_type;
|
||||
code = req?.headers?.code;
|
||||
redirect_uri = req?.headers?.redirect_uri;
|
||||
client_id = req?.headers?.client_id;
|
||||
client_secret = req?.headers?.client_secret;
|
||||
}
|
||||
|
||||
if (grant_type !== 'authorization_code') {
|
||||
return res.status(401).json({ error: 'Invalid Grant Type' });
|
||||
}
|
||||
|
||||
// const client = clients.find((c) => c.client_id === client_id && c.client_secret === client_secret);
|
||||
// if (!client) {
|
||||
// return res.status(401).json({ error: 'Invalid client credentials' });
|
||||
// }
|
||||
|
||||
const storedAuthCode = authCodes.find((t) => t.authCode === code);
|
||||
|
||||
if (!storedAuthCode) {
|
||||
return res.status(401).json({ error: 'Invalid Authorization Code' });
|
||||
}
|
||||
|
||||
const accessToken = generateUniqueString();
|
||||
tokens.push({
|
||||
accessToken: accessToken,
|
||||
client_id
|
||||
});
|
||||
|
||||
res.json({ access_token: accessToken });
|
||||
});
|
||||
|
||||
router.post('/resource', (req, res) => {
|
||||
try {
|
||||
const { token } = req.query;
|
||||
const storedToken = tokens.find((t) => t.accessToken === token);
|
||||
if (!storedToken) {
|
||||
return res.status(401).json({ error: 'Invalid Access Token' });
|
||||
}
|
||||
return res.json({ resource: { name: 'foo', email: 'foo@bar.com' } });
|
||||
} catch (err) {
|
||||
return res.status(401).json({ error: 'Corrupt Access Token' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
62
packages/bruno-tests/src/auth/oauth2/cc.js
Normal file
62
packages/bruno-tests/src/auth/oauth2/cc.js
Normal file
@ -0,0 +1,62 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const crypto = require('crypto');
|
||||
const clients = [
|
||||
{
|
||||
client_id: 'client_id_1',
|
||||
client_secret: 'client_secret_1'
|
||||
}
|
||||
];
|
||||
|
||||
const tokens = [];
|
||||
|
||||
function generateUniqueString() {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
router.post('/token', (req, res) => {
|
||||
let grant_type, client_id, client_secret;
|
||||
if (req?.body?.grant_type) {
|
||||
grant_type = req?.body?.grant_type;
|
||||
client_id = req?.body?.client_id;
|
||||
client_secret = req?.body?.client_secret;
|
||||
} else if (req?.headers?.grant_type) {
|
||||
grant_type = req?.headers?.grant_type;
|
||||
client_id = req?.headers?.client_id;
|
||||
client_secret = req?.headers?.client_secret;
|
||||
}
|
||||
|
||||
if (grant_type !== 'client_credentials') {
|
||||
return res.status(401).json({ error: 'Invalid Grant Type, expected "client_credentials"' });
|
||||
}
|
||||
|
||||
const client = clients.find((c) => c.client_id == client_id && c.client_secret == client_secret);
|
||||
|
||||
if (!client) {
|
||||
return res.status(401).json({ error: 'Invalid client' });
|
||||
}
|
||||
|
||||
const token = generateUniqueString();
|
||||
tokens.push({
|
||||
token,
|
||||
client_id,
|
||||
client_secret
|
||||
});
|
||||
|
||||
return res.json({ message: 'Authenticated successfully', access_token: token });
|
||||
});
|
||||
|
||||
router.get('/resource', (req, res) => {
|
||||
try {
|
||||
const { token } = req.query;
|
||||
const storedToken = tokens.find((t) => t.token === token);
|
||||
if (!storedToken) {
|
||||
return res.status(401).json({ error: 'Invalid Access Token' });
|
||||
}
|
||||
return res.json({ resource: { name: 'foo', email: 'foo@bar.com' } });
|
||||
} catch (err) {
|
||||
return res.status(401).json({ error: 'Corrupt Access Token' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
59
packages/bruno-tests/src/auth/oauth2/ropc.js
Normal file
59
packages/bruno-tests/src/auth/oauth2/ropc.js
Normal file
@ -0,0 +1,59 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const users = [
|
||||
{
|
||||
username: 'foo',
|
||||
password: 'bar'
|
||||
}
|
||||
];
|
||||
|
||||
// P
|
||||
// {
|
||||
// grant_type: 'password',
|
||||
// username: 'foo',
|
||||
// password: 'bar'
|
||||
// }
|
||||
|
||||
// I
|
||||
// {
|
||||
// grant_type: 'password',
|
||||
// username: 'foo',
|
||||
// password: 'bar',
|
||||
// client_id: 'client_id_1',
|
||||
// client_secret: 'client_secret_1'
|
||||
// }
|
||||
router.post('/token', (req, res) => {
|
||||
const { grant_type, username, password, client_id, client_secret } = req.body;
|
||||
|
||||
if (grant_type !== 'password') {
|
||||
return res.status(401).json({ error: 'Invalid Grant Type' });
|
||||
}
|
||||
|
||||
const user = users.find((u) => u.username == username && u.password == password);
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'Invalid user credentials' });
|
||||
}
|
||||
var token = jwt.sign({ username, password }, 'bruno');
|
||||
return res.json({ message: 'Authorization successful', access_token: token });
|
||||
});
|
||||
|
||||
router.post('/resource', (req, res) => {
|
||||
try {
|
||||
const tokenString = req.header('Authorization');
|
||||
const token = tokenString.split(' ')[1];
|
||||
var decodedJwt = jwt.verify(token, 'bruno');
|
||||
const { username, password } = decodedJwt;
|
||||
const user = users.find((u) => u.username === username && u.password === password);
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'Invalid token' });
|
||||
}
|
||||
return res.json({ resource: { name: 'foo', email: 'foo@bar.com' } });
|
||||
} catch (err) {
|
||||
return res.status(401).json({ error: 'Corrupt token' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
@ -5,7 +5,7 @@ const cors = require('cors');
|
||||
const multer = require('multer');
|
||||
|
||||
const app = new express();
|
||||
const port = process.env.PORT || 80;
|
||||
const port = process.env.PORT || 8080;
|
||||
const upload = multer();
|
||||
|
||||
app.use(cors());
|
||||
|
Loading…
Reference in New Issue
Block a user