Browse Source

Adding authenticated plugins and navigations endpoint

bodicsek 3 years ago
parent
commit
86adb175b6

+ 26 - 0
data/navigationRegistry.json

@@ -0,0 +1,26 @@
+{
+    "default": [
+        {
+            "labelKey": "Links",
+            "children": [
+                {
+                    "labelKey": "Oracle Store",
+                    "path": "https://shop-dev2.oraclecorp.com"
+                }
+            ]
+        },
+        {
+            "labelKey": "Plugins",
+            "children": [
+                {
+                    "labelKey": "Dashboard",
+                    "pluginName": "dashboard"
+                },
+                {
+                    "labelKey": "Plugin A",
+                    "pluginName": "plugin-a"
+                }
+            ]
+        }
+    ]
+}

+ 12 - 0
data/pluginRegistry.json

@@ -0,0 +1,12 @@
+{
+    "dashboard": {
+        "name": "dashboard",
+        "url": "https://localhost:8000",
+        "path": "/"
+    },
+    "plugin-a": {
+        "name": "plugin-a",
+        "url": "https://localhost:8001",
+        "path": "/plugin-a"
+    }
+}

+ 382 - 6
package-lock.json

@@ -1,18 +1,22 @@
 {
   "name": "occ-fw-backend",
-  "version": "0.0.1",
+  "version": "0.0.2",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "occ-fw-backend",
-      "version": "0.0.1",
+      "version": "0.0.2",
       "license": "UNLICENSED",
       "dependencies": {
         "@nestjs/axios": "^0.1.0",
         "@nestjs/common": "^9.0.0",
         "@nestjs/core": "^9.0.0",
+        "@nestjs/mapped-types": "*",
+        "@nestjs/passport": "^9.0.0",
         "@nestjs/platform-express": "^9.0.0",
+        "passport": "^0.6.0",
+        "passport-jwt": "^4.0.0",
         "reflect-metadata": "^0.1.13",
         "rimraf": "^3.0.2",
         "rxjs": "^7.2.0"
@@ -24,6 +28,7 @@
         "@types/express": "^4.17.13",
         "@types/jest": "28.1.8",
         "@types/node": "^16.0.0",
+        "@types/passport-jwt": "^3.0.7",
         "@types/supertest": "^2.0.11",
         "@typescript-eslint/eslint-plugin": "^5.0.0",
         "@typescript-eslint/parser": "^5.0.0",
@@ -1558,6 +1563,34 @@
         }
       }
     },
+    "node_modules/@nestjs/mapped-types": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.2.0.tgz",
+      "integrity": "sha512-NTFwPZkQWsArQH8QSyFWGZvJ08gR+R4TofglqZoihn/vU+ktHEJjMqsIsADwb7XD97DhiD+TVv5ac+jG33BHrg==",
+      "peerDependencies": {
+        "@nestjs/common": "^7.0.8 || ^8.0.0 || ^9.0.0",
+        "class-transformer": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0",
+        "class-validator": "^0.11.1 || ^0.12.0 || ^0.13.0",
+        "reflect-metadata": "^0.1.12"
+      },
+      "peerDependenciesMeta": {
+        "class-transformer": {
+          "optional": true
+        },
+        "class-validator": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/passport": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-9.0.0.tgz",
+      "integrity": "sha512-Gnh8n1wzFPOLSS/94X1sUP4IRAoXTgG4odl7/AO5h+uwscEGXxJFercrZfqdAwkWhqkKWbsntM3j5mRy/6ZQDA==",
+      "peerDependencies": {
+        "@nestjs/common": "^8.0.0 || ^9.0.0",
+        "passport": "^0.4.0 || ^0.5.0 || ^0.6.0"
+      }
+    },
     "node_modules/@nestjs/platform-express": {
       "version": "9.1.4",
       "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.1.4.tgz",
@@ -1980,6 +2013,15 @@
       "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
+    "node_modules/@types/jsonwebtoken": {
+      "version": "8.5.9",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz",
+      "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/mime": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@@ -1998,6 +2040,36 @@
       "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
       "dev": true
     },
+    "node_modules/@types/passport": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz",
+      "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/passport-jwt": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.7.tgz",
+      "integrity": "sha512-qRQ4qlww1Yhs3IaioDKrsDNmKy6gLDLgFsGwpCnc2YqWovO2Oxu9yCQdWHMJafQ7UIuOba4C4/TNXcGkQfEjlQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/jsonwebtoken": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "node_modules/@types/passport-strategy": {
+      "version": "0.2.35",
+      "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
+      "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
+      "dev": true,
+      "dependencies": {
+        "@types/express": "*",
+        "@types/passport": "*"
+      }
+    },
     "node_modules/@types/prettier": {
       "version": "2.7.1",
       "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
@@ -2912,6 +2984,11 @@
         "ieee754": "^1.1.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/buffer-from": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3444,6 +3521,14 @@
         "node": ">=6.0.0"
       }
     },
+    "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",
@@ -5800,6 +5885,54 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/jsonwebtoken": {
+      "version": "8.5.1",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+      "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+      "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": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=4",
+        "npm": ">=1.4.28"
+      }
+    },
+    "node_modules/jsonwebtoken/node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "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/kleur": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -5867,6 +6000,36 @@
       "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
       "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",
@@ -5879,6 +6042,11 @@
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "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/log-symbols": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@@ -6119,8 +6287,7 @@
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "dev": true
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
     "node_modules/multer": {
       "version": "1.4.4-lts.1",
@@ -6448,6 +6615,40 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/passport": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
+      "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
+      "dependencies": {
+        "passport-strategy": "1.x.x",
+        "pause": "0.0.1",
+        "utils-merge": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/jaredhanson"
+      }
+    },
+    "node_modules/passport-jwt": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
+      "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==",
+      "dependencies": {
+        "jsonwebtoken": "^8.2.0",
+        "passport-strategy": "^1.0.0"
+      }
+    },
+    "node_modules/passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -6494,6 +6695,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
+    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -9500,6 +9706,18 @@
         "uuid": "9.0.0"
       }
     },
+    "@nestjs/mapped-types": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.2.0.tgz",
+      "integrity": "sha512-NTFwPZkQWsArQH8QSyFWGZvJ08gR+R4TofglqZoihn/vU+ktHEJjMqsIsADwb7XD97DhiD+TVv5ac+jG33BHrg==",
+      "requires": {}
+    },
+    "@nestjs/passport": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-9.0.0.tgz",
+      "integrity": "sha512-Gnh8n1wzFPOLSS/94X1sUP4IRAoXTgG4odl7/AO5h+uwscEGXxJFercrZfqdAwkWhqkKWbsntM3j5mRy/6ZQDA==",
+      "requires": {}
+    },
     "@nestjs/platform-express": {
       "version": "9.1.4",
       "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.1.4.tgz",
@@ -9858,6 +10076,15 @@
       "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
+    "@types/jsonwebtoken": {
+      "version": "8.5.9",
+      "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz",
+      "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/mime": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@@ -9876,6 +10103,36 @@
       "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
       "dev": true
     },
+    "@types/passport": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.11.tgz",
+      "integrity": "sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*"
+      }
+    },
+    "@types/passport-jwt": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.7.tgz",
+      "integrity": "sha512-qRQ4qlww1Yhs3IaioDKrsDNmKy6gLDLgFsGwpCnc2YqWovO2Oxu9yCQdWHMJafQ7UIuOba4C4/TNXcGkQfEjlQ==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*",
+        "@types/jsonwebtoken": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "@types/passport-strategy": {
+      "version": "0.2.35",
+      "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
+      "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
+      "dev": true,
+      "requires": {
+        "@types/express": "*",
+        "@types/passport": "*"
+      }
+    },
     "@types/prettier": {
       "version": "2.7.1",
       "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
@@ -10564,6 +10821,11 @@
         "ieee754": "^1.1.13"
       }
     },
+    "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=="
+    },
     "buffer-from": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -10955,6 +11217,14 @@
         "esutils": "^2.0.2"
       }
     },
+    "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==",
+      "requires": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -12721,6 +12991,49 @@
         "universalify": "^2.0.0"
       }
     },
+    "jsonwebtoken": {
+      "version": "8.5.1",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+      "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+      "requires": {
+        "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": "^5.6.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+          "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+        }
+      }
+    },
+    "jwa": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+      "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+      "requires": {
+        "buffer-equal-constant-time": "1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "jws": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+      "requires": {
+        "jwa": "^1.4.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
     "kleur": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -12770,6 +13083,36 @@
       "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
       "dev": true
     },
+    "lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+    },
+    "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=="
+    },
+    "lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+    },
+    "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=="
+    },
+    "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=="
+    },
+    "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=="
+    },
     "lodash.memoize": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -12782,6 +13125,11 @@
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true
     },
+    "lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+    },
     "log-symbols": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@@ -12954,8 +13302,7 @@
     "ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "dev": true
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
     "multer": {
       "version": "1.4.4-lts.1",
@@ -13193,6 +13540,30 @@
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
       "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
     },
+    "passport": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
+      "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
+      "requires": {
+        "passport-strategy": "1.x.x",
+        "pause": "0.0.1",
+        "utils-merge": "^1.0.1"
+      }
+    },
+    "passport-jwt": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
+      "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==",
+      "requires": {
+        "jsonwebtoken": "^8.2.0",
+        "passport-strategy": "^1.0.0"
+      }
+    },
+    "passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA=="
+    },
     "path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -13227,6 +13598,11 @@
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
+    "pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
+    },
     "picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",

+ 6 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "occ-fw-backend",
-  "version": "0.0.2",
+  "version": "0.0.3",
   "description": "",
   "author": "",
   "private": true,
@@ -32,7 +32,11 @@
     "@nestjs/axios": "^0.1.0",
     "@nestjs/common": "^9.0.0",
     "@nestjs/core": "^9.0.0",
+    "@nestjs/mapped-types": "*",
+    "@nestjs/passport": "^9.0.0",
     "@nestjs/platform-express": "^9.0.0",
+    "passport": "^0.6.0",
+    "passport-jwt": "^4.0.0",
     "reflect-metadata": "^0.1.13",
     "rimraf": "^3.0.2",
     "rxjs": "^7.2.0"
@@ -44,6 +48,7 @@
     "@types/express": "^4.17.13",
     "@types/jest": "28.1.8",
     "@types/node": "^16.0.0",
+    "@types/passport-jwt": "^3.0.7",
     "@types/supertest": "^2.0.11",
     "@typescript-eslint/eslint-plugin": "^5.0.0",
     "@typescript-eslint/parser": "^5.0.0",

+ 3 - 1
src/app.module.ts

@@ -2,9 +2,11 @@ import { Module } from '@nestjs/common';
 import { AppController } from './app.controller';
 import { AppService } from './app.service';
 import { AuthModule } from './auth/auth.module';
+import { PluginsModule } from './plugins/plugins.module';
+import { NavigationsModule } from './navigations/navigations.module';
 
 @Module({
-  imports: [AuthModule],
+  imports: [AuthModule, PluginsModule, NavigationsModule],
   controllers: [AppController],
   providers: [AppService],
 })

+ 42 - 0
src/auth/auth.jwt.strategy.ts

@@ -0,0 +1,42 @@
+
+import { ExtractJwt, Strategy } from 'passport-jwt';
+import { PassportStrategy } from '@nestjs/passport';
+import { Injectable } from '@nestjs/common';
+
+const publicKey =
+`-----BEGIN CERTIFICATE-----
+MIIDZTCCAk2gAwIBAgIGAXc3MMY+MA0GCSqGSIb3DQEBCwUAMFkxEzARBgoJkiaJ
+k/IsZAEZFgNjb20xFjAUBgoJkiaJk/IsZAEZFgZvcmFjbGUxFTATBgoJkiaJk/Is
+ZAEZFgVjbG91ZDETMBEGA1UEAxMKQ2xvdWQ5Q0EtMjAeFw0yMTAxMjUwMTM3NTla
+Fw0zMTAxMjUwMTM3NTlaMFoxEzARBgNVBAMTCnNzbERvbWFpbnMxDzANBgNVBAMT
+BkNsb3VkOTEyMDAGA1UEAwwpaWRjcy1sb2dpbi1zdGFnZV8yNS8wMS8yMDIxXzAx
+OjM3OjU4X2tleXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCygS+u
+0gsASQ+DhsAw7VwWeLI4UrOIKDj8sE8eeprmBrfhkbJCJmyI2yMGj7qtEgg/8+TP
+YQazP2bmrc3+gxY3i9XxVuv4Ka2LlbBEnHGpzY6IgY9fIFRHJceDq8cidgPUE5in
+N7dVHali/E64V0nY+LWIjVQBUNJRVvfPihTd1evXmaNKyPCcokA8Tk4kPms36/b3
+ASO3rmJ9txKLwC9ed5R6zKXWbtZ2okUFcQ1tbrfmYGs33bw6YX6rPw2048yJeXEY
+N1hDvvzTm66+MA2VbbJ9adzYxCrJankzlWr2y+cOCUh+ZkOqoI6ZUXcgAA3RV+Tw
+d1KYZFDfholkkRIZAgMBAAGjMjAwMA8GA1UdDwEB/wQFAwMH+AAwHQYDVR0OBBYE
+FPuOhtHfFs6QU8fxsbcM9WpeXyhiMA0GCSqGSIb3DQEBCwUAA4IBAQAwWm9VGXa0
+sSY/gz8X/F94RUpJpGtLJPhpcHqe4kWks3vrSW9FG2yXMulc+keaqAzU6caJowzy
+k0V2vcgkpM2Ol/HQjO9zDpNZVjU+VkTJcuQjgIW/8FYbFfd0WPmQFVysOXDBPQs+
+Jf1aCAuIER/Pfo4RTGMnhFxT7sSyyWa5AveP2fwSGrMZj+FcTBw6vdT1EF6jC/NW
+DyUdBejZ4xWTed5qDSbOR971jLbBbLm/hZtXhy3pz8iFaVobRc6w4Uru2nht55KA
+MFc9PSa5ZeYJB0MU8/8sqpXlChFrlsHuTQW9Mj/ZukxOPK+/WQAgJIsfDwBUdjLr
+pMc56p6t/EzQ
+-----END CERTIFICATE-----`;
+
+@Injectable()
+export class JwtStrategy extends PassportStrategy(Strategy) {
+  constructor() {
+    super({
+      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+      ignoreExpiration: false,
+      secretOrKey: publicKey
+    });
+  }
+
+  async validate(payload: any) {
+    return { userId: payload.sub, username: payload.user_displayname };
+  }
+}

+ 2 - 1
src/auth/auth.module.ts

@@ -1,11 +1,12 @@
 import { HttpModule } from '@nestjs/axios';
 import { Module } from '@nestjs/common';
 import { AuthController } from './auth.controller';
+import { JwtStrategy } from './auth.jwt.strategy';
 import { AuthService } from './auth.service';
 
 @Module({
   imports: [HttpModule],
   controllers: [AuthController],
-  providers: [AuthService],
+  providers: [AuthService, JwtStrategy],
 })
 export class AuthModule {}

+ 19 - 0
src/auth/jwt-auth.guard.ts

@@ -0,0 +1,19 @@
+import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
+
+@Injectable()
+export class JwtAuthGuard extends AuthGuard('jwt') {
+    canActivate(context: ExecutionContext) {
+        // Add your custom authentication logic here
+        // for example, call super.logIn(request) to establish a session.
+        return super.canActivate(context);
+    }
+
+    handleRequest(err, user, info) {
+        // You can throw an exception based on either "info" or "err" arguments
+        if (err || !user) {
+            throw err || new UnauthorizedException();
+        }
+        return user;
+    }
+}

+ 16 - 0
src/navigations/interfaces/navigation.interface.ts

@@ -0,0 +1,16 @@
+export interface BaseItem {
+    labelKey: string;
+    children?: NavigationItem[];
+}
+
+export interface LinkItem extends BaseItem {
+    path: string;
+    openInNewTab?: boolean;
+}
+
+export interface PluginItem extends BaseItem {
+    pluginName: string;
+    appendPath?: string;
+}
+
+export type NavigationItem = LinkItem | PluginItem;

+ 20 - 0
src/navigations/navigations.controller.spec.ts

@@ -0,0 +1,20 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { NavigationsController } from './navigations.controller';
+import { NavigationsService } from './navigations.service';
+
+describe('NaviagtionsController', () => {
+  let controller: NavigationsController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [NavigationsController],
+      providers: [NavigationsService],
+    }).compile();
+
+    controller = module.get<NavigationsController>(NavigationsController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});

+ 14 - 0
src/navigations/navigations.controller.ts

@@ -0,0 +1,14 @@
+import { Controller, Get, UseGuards } from '@nestjs/common';
+import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
+import { NavigationsService } from './navigations.service';
+
+@Controller('navigations')
+export class NavigationsController {
+  constructor(private readonly navigationsService: NavigationsService) {}
+
+  @Get()
+  @UseGuards(JwtAuthGuard)
+  listNavigations() {
+    return this.navigationsService.listNavigations();
+  }
+}

+ 9 - 0
src/navigations/navigations.module.ts

@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+import { NavigationsService } from './navigations.service';
+import { NavigationsController } from './navigations.controller';
+
+@Module({
+  controllers: [NavigationsController],
+  providers: [NavigationsService]
+})
+export class NavigationsModule {}

+ 18 - 0
src/navigations/navigations.service.spec.ts

@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { NavigationsService } from './navigations.service';
+
+describe('NaviagtionsService', () => {
+  let service: NavigationsService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [NavigationsService],
+    }).compile();
+
+    service = module.get<NavigationsService>(NavigationsService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});

+ 16 - 0
src/navigations/navigations.service.ts

@@ -0,0 +1,16 @@
+import { Injectable } from '@nestjs/common';
+import { readFileSync } from 'fs';
+import { NavigationItem } from "./interfaces/navigation.interface";
+
+@Injectable()
+export class NavigationsService {
+    private navigationRegistry: { [key: string]: NavigationItem[] };
+
+    constructor() {
+        this.navigationRegistry = JSON.parse(readFileSync("data/navigationRegistry.json").toString());
+    }
+
+    listNavigations() {
+        return this.navigationRegistry["default"];
+    }
+}

+ 15 - 0
src/plugins/interfaces/plugin.interface.ts

@@ -0,0 +1,15 @@
+/**
+ * @prop name the name of the plugin.
+ * @prop path the router path mapped to this plugin.
+ * @prop url of the plugin.
+ * @prop bootstrapUrl the bootstrap url of the plugin if e.g. session is required
+ *
+ * @export
+ * @interface Plugin
+ */
+export interface Plugin {
+    name: string;
+    path: string;
+    url: string;
+    bootstrapUrl?: string;
+}

+ 20 - 0
src/plugins/plugins.controller.spec.ts

@@ -0,0 +1,20 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { PluginsController } from './plugins.controller';
+import { PluginsService } from './plugins.service';
+
+describe('PluginsController', () => {
+  let controller: PluginsController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [PluginsController],
+      providers: [PluginsService],
+    }).compile();
+
+    controller = module.get<PluginsController>(PluginsController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});

+ 14 - 0
src/plugins/plugins.controller.ts

@@ -0,0 +1,14 @@
+import { Controller, Get, UseGuards } from '@nestjs/common';
+import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
+import { PluginsService } from './plugins.service';
+
+@Controller('plugins')
+export class PluginsController {
+  constructor(private readonly pluginsService: PluginsService) {}
+
+  @Get()
+  @UseGuards(JwtAuthGuard)
+  listPlugins() {
+    return this.pluginsService.listPlugins();
+  }
+}

+ 9 - 0
src/plugins/plugins.module.ts

@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+import { PluginsService } from './plugins.service';
+import { PluginsController } from './plugins.controller';
+
+@Module({
+  controllers: [PluginsController],
+  providers: [PluginsService]
+})
+export class PluginsModule {}

+ 18 - 0
src/plugins/plugins.service.spec.ts

@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { PluginsService } from './plugins.service';
+
+describe('PluginsService', () => {
+  let service: PluginsService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [PluginsService],
+    }).compile();
+
+    service = module.get<PluginsService>(PluginsService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});

+ 16 - 0
src/plugins/plugins.service.ts

@@ -0,0 +1,16 @@
+import { Injectable } from '@nestjs/common';
+import { readFileSync } from 'fs';
+import { Plugin } from './interfaces/plugin.interface';
+
+@Injectable()
+export class PluginsService {
+    private pluginRegistry: { [key: string]: Plugin };
+
+    constructor() {
+        this.pluginRegistry = JSON.parse(readFileSync("data/pluginRegistry.json").toString());
+    }
+
+    listPlugins() {
+        return Object.values(this.pluginRegistry);
+    }
+}