diff --git a/docker-compose.yml b/docker-compose.yml index 1836a6c4..ce09a346 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,7 @@ services: - SM_IP=localhost # - GOOGLE_RECAPTCHA= # - ALLOW_INTERNAL_AUTH=false + # - ALLOW_SAML_AUTH=false # - ALLOW_HOME_VIEW=true # - GITHUB_CLIENTID= # - BITBUCKET_CLIENTID= diff --git a/docker/setup.sh b/docker/setup.sh index 87d119c0..bf2a8409 100644 --- a/docker/setup.sh +++ b/docker/setup.sh @@ -13,6 +13,7 @@ echo "SWITCHERAPI_URL = ${SWITCHERAPI_URL}" echo "SM_IP = ${SM_IP}" echo "GOOGLE_RECAPTCHA = ${GOOGLE_RECAPTCHA}" echo "ALLOW_INTERNAL_AUTH = ${ALLOW_INTERNAL_AUTH}" +echo "ALLOW_SAML_AUTH = ${ALLOW_SAML_AUTH}" echo "ALLOW_HOME_VIEW = ${ALLOW_HOME_VIEW}" echo "GITHUB_CLIENTID = ${GITHUB_CLIENTID}" echo "BITBUCKET_CLIENTID = ${BITBUCKET_CLIENTID}" diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 12adfb46..f3a96ef0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -37,9 +37,9 @@ "@angular/cli": "^20.3.1", "@angular/compiler-cli": "~20.3.0", "@angular/language-service": "20.3.0", - "@types/node": "^24.3.1", - "@types/react": "^19.1.12", - "angular-eslint": "20.2.0", + "@types/node": "^24.4.0", + "@types/react": "^19.1.13", + "angular-eslint": "20.3.0", "eslint": "^9.35.0", "ts-node": "^10.9.2", "typescript": "^5.9.2", @@ -485,9 +485,9 @@ } }, "node_modules/@angular-eslint/builder": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-20.2.0.tgz", - "integrity": "sha512-0qej+U/u5MDnvnUhrWAXmXJd7ZliZzYQtkmy50ypq/LaQwkOuZBEFh9EqlZ1k4n8n2DKQou03KJmKSoAqc/I8A==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-20.3.0.tgz", + "integrity": "sha512-3XpWLdh+/K4+r0ChkKW00SXWyBA7ShMpE+Pt1XUmIu4srJgGRnt8e+kC4Syi+s2t5QS7PjlwRaelB1KfSMXZ5A==", "dev": true, "license": "MIT", "dependencies": { @@ -500,21 +500,21 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-20.2.0.tgz", - "integrity": "sha512-9NhytRavpxWqa0fK+mlQZrif91MhtG3VEV3JCQEwOH9JPueY95XVHYwPgcbODhoSg/z5YaTVby5G254cEXUMew==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-20.3.0.tgz", + "integrity": "sha512-QwuNnmRNr/uNj89TxknPbGcs5snX1w7RoJJPNAsfb2QGcHzUTQovS8hqm9kaDZdpUJDPP7jt7B6F0+EjrPAXRA==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-20.2.0.tgz", - "integrity": "sha512-HdujUz7Q1ZW371cCJRkUcp0bjU/iP8Z/ZNTStCzMd4euu+HwVt69dLsTCs6f1i6SMqlIUjaP8TbqNo5nV8Altw==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-20.3.0.tgz", + "integrity": "sha512-7ghzGTiExrgTetDQ6IPP5uXSa94Xhtzp2VHCIa58EcUb7oMv06HWZ1Uss3xgFmACsLpN+vayKJIdFiboqaGVRA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.2.0", - "@angular-eslint/utils": "20.2.0", + "@angular-eslint/bundled-angular-compiler": "20.3.0", + "@angular-eslint/utils": "20.3.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { @@ -524,19 +524,19 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-20.2.0.tgz", - "integrity": "sha512-pRuROa9QUUIq/ulB5rbXrwOhFA1tcR8HhGq187gFQfPno/bFZfbF9R8x+zukbVipNjl087WHUWj09KNDcJBLlA==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-20.3.0.tgz", + "integrity": "sha512-WMJDJfybOLCiN4QrOyrLl+Zt5F+A/xoDYMWTdn+LgACheLs2tguVQiwf+oCgHnHGcsTsulPYlRHldKBGZMgs4w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.2.0", - "@angular-eslint/utils": "20.2.0", + "@angular-eslint/bundled-angular-compiler": "20.3.0", + "@angular-eslint/utils": "20.3.0", "aria-query": "5.3.2", "axobject-query": "4.1.0" }, "peerDependencies": { - "@angular-eslint/template-parser": "20.2.0", + "@angular-eslint/template-parser": "20.3.0", "@typescript-eslint/types": "^7.11.0 || ^8.0.0", "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", @@ -544,29 +544,29 @@ } }, "node_modules/@angular-eslint/schematics": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-20.2.0.tgz", - "integrity": "sha512-vAslYgJ2Rs2xY80ckwbuv/YWpEO9d/lFMq8CGrm37PI0IB5uRuGVWxaVboBLP6WUj9iMS/ufZUcCu0fdQ05V8Q==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-20.3.0.tgz", + "integrity": "sha512-4n92tHKIJm1PP+FjhnmO7AMpvKdRIoF+YgF38oUU7aMJqfZ3RXIhazMMxw2u3VU1MisKH766KSll++c4LgarVA==", "dev": true, "license": "MIT", "dependencies": { "@angular-devkit/core": ">= 20.0.0 < 21.0.0", "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", - "@angular-eslint/eslint-plugin": "20.2.0", - "@angular-eslint/eslint-plugin-template": "20.2.0", + "@angular-eslint/eslint-plugin": "20.3.0", + "@angular-eslint/eslint-plugin-template": "20.3.0", "ignore": "7.0.5", "semver": "7.7.2", "strip-json-comments": "3.1.1" } }, "node_modules/@angular-eslint/template-parser": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-20.2.0.tgz", - "integrity": "sha512-72hskYThlVhktpRCwSwAohY/SxUoMv0hhS71zjlJcHFTzTAWCI8Zy2U4OJuhUO7+XWL6iAu13NKzJKRzUhGdSw==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-20.3.0.tgz", + "integrity": "sha512-gB564h/kZ7siWvgHDETU++sk5e25qFfVaizLaa6KoBEYFP6dOCiedz15LTcA0TsXp0rGu6Z6zkl291iSM1qzDA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.2.0", + "@angular-eslint/bundled-angular-compiler": "20.3.0", "eslint-scope": "^8.0.2" }, "peerDependencies": { @@ -575,13 +575,13 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-20.2.0.tgz", - "integrity": "sha512-GnEa8BU9xBLUq4JQ8UgXecUXPCmju9P5KIobql17LV1t3vnJ33Zr7acO1jWOzluypllKSVrtARdRTI+TQGCqrA==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-20.3.0.tgz", + "integrity": "sha512-7XOQeNXgyhznDwoP1TwPrCMq/uXKJHQgCVPFREkJGKbNf/jzNldB7iV1eqpBzUQIPEQFgfcDG67dexpMAq3N4g==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.2.0" + "@angular-eslint/bundled-angular-compiler": "20.3.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -3526,17 +3526,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@inquirer/ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.0.tgz", + "integrity": "sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@inquirer/checkbox": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.2.tgz", - "integrity": "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.4.tgz", + "integrity": "sha512-2n9Vgf4HSciFq8ttKXk+qy+GsyTXPV1An6QAwe/8bkbbqvG4VW1I/ZY1pNu2rf+h9bdzMLPbRSfcNxkHBy/Ydw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -3574,15 +3584,15 @@ } }, "node_modules/@inquirer/core": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", - "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.2.tgz", + "integrity": "sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==", "dev": true, "license": "MIT", "dependencies": { + "@inquirer/ansi": "^1.0.0", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", @@ -3602,14 +3612,14 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.18", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz", - "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==", + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.20.tgz", + "integrity": "sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/external-editor": "^1.0.1", + "@inquirer/core": "^10.2.2", + "@inquirer/external-editor": "^1.0.2", "@inquirer/type": "^3.0.8" }, "engines": { @@ -3625,13 +3635,13 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.18.tgz", - "integrity": "sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==", + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.20.tgz", + "integrity": "sha512-Dt9S+6qUg94fEvgn54F2Syf0Z3U8xmnBI9ATq2f5h9xt09fs2IJXSCIXyyVHwvggKWFXEY/7jATRo2K6Dkn6Ow==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", + "@inquirer/core": "^10.2.2", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, @@ -3648,14 +3658,14 @@ } }, "node_modules/@inquirer/external-editor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", - "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz", + "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", "dev": true, "license": "MIT", "dependencies": { "chardet": "^2.1.0", - "iconv-lite": "^0.6.3" + "iconv-lite": "^0.7.0" }, "engines": { "node": ">=18" @@ -3680,13 +3690,13 @@ } }, "node_modules/@inquirer/input": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.2.tgz", - "integrity": "sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.4.tgz", + "integrity": "sha512-cwSGpLBMwpwcZZsc6s1gThm0J+it/KIJ+1qFL2euLmSKUMGumJ5TcbMgxEjMjNHRGadouIYbiIgruKoDZk7klw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", + "@inquirer/core": "^10.2.2", "@inquirer/type": "^3.0.8" }, "engines": { @@ -3702,13 +3712,13 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.18.tgz", - "integrity": "sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.20.tgz", + "integrity": "sha512-bbooay64VD1Z6uMfNehED2A2YOPHSJnQLs9/4WNiV/EK+vXczf/R988itL2XLDGTgmhMF2KkiWZo+iEZmc4jqg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", + "@inquirer/core": "^10.2.2", "@inquirer/type": "^3.0.8" }, "engines": { @@ -3724,15 +3734,15 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.18.tgz", - "integrity": "sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==", + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.20.tgz", + "integrity": "sha512-nxSaPV2cPvvoOmRygQR+h0B+Av73B01cqYLcr7NXcGXhbmsYfUb8fDdw2Us1bI2YsX+VvY7I7upgFYsyf8+Nug==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2" + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -3777,13 +3787,13 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.6.tgz", - "integrity": "sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.8.tgz", + "integrity": "sha512-CQ2VkIASbgI2PxdzlkeeieLRmniaUU1Aoi5ggEdm6BIyqopE9GuDXdDOj9XiwOqK5qm72oI2i6J+Gnjaa26ejg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", + "@inquirer/core": "^10.2.2", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, @@ -3800,13 +3810,13 @@ } }, "node_modules/@inquirer/search": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.1.tgz", - "integrity": "sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.3.tgz", + "integrity": "sha512-D5T6ioybJJH0IiSUK/JXcoRrrm8sXwzrVMjibuPs+AgxmogKslaafy1oxFiorNI4s3ElSkeQZbhYQgLqiL8h6Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", + "@inquirer/core": "^10.2.2", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" @@ -3824,16 +3834,16 @@ } }, "node_modules/@inquirer/select": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.2.tgz", - "integrity": "sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.4.tgz", + "integrity": "sha512-Qp20nySRmfbuJBBsgPU7E/cL62Hf250vMZRzYDcBHty2zdD1kKCnoDFWRr0WO2ZzaXp3R7a4esaVGJUx0E6zvA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.0", + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", "@inquirer/figures": "^1.0.13", "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -4745,16 +4755,16 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.4.tgz", - "integrity": "sha512-+ZEtJPp8EF8h4kN6rLQECRor00H7jtDgBVtttIUoxuDkXLiQMaSBqju3LV/IEsMvqVG5pviUvR4jYhIA1xNm8w==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.5.tgz", + "integrity": "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", - "@tybys/wasm-util": "^0.10.0" + "@tybys/wasm-util": "^0.10.1" } }, "node_modules/@ng-bootstrap/ng-bootstrap": { @@ -6239,9 +6249,9 @@ } }, "node_modules/@types/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", "license": "MIT", "optional": true }, @@ -6583,13 +6593,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", - "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "version": "24.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.4.0.tgz", + "integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.10.0" + "undici-types": "~7.11.0" } }, "node_modules/@types/node-forge": { @@ -6617,9 +6627,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", - "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", + "version": "19.1.13", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz", + "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7358,19 +7368,19 @@ } }, "node_modules/angular-eslint": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-20.2.0.tgz", - "integrity": "sha512-DaBdj55ykBQOExCzcSJUOdTEJaBbV2+y6gCfgMopWoR2q6aUrH2XzxvbN4gEAYOfIrS22VP5ugk4/QTu9gwPfg==", + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-20.3.0.tgz", + "integrity": "sha512-MvmeFuPmJHRmfL1A9IMtZJEYaU6sF++saJgpsU7aOD6YDZCGJ0J6HxlJ/q7YRbWYuI1q+gF/qALxdnuwHYadSg==", "dev": true, "license": "MIT", "dependencies": { "@angular-devkit/core": ">= 20.0.0 < 21.0.0", "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", - "@angular-eslint/builder": "20.2.0", - "@angular-eslint/eslint-plugin": "20.2.0", - "@angular-eslint/eslint-plugin-template": "20.2.0", - "@angular-eslint/schematics": "20.2.0", - "@angular-eslint/template-parser": "20.2.0", + "@angular-eslint/builder": "20.3.0", + "@angular-eslint/eslint-plugin": "20.3.0", + "@angular-eslint/eslint-plugin-template": "20.3.0", + "@angular-eslint/schematics": "20.3.0", + "@angular-eslint/template-parser": "20.3.0", "@typescript-eslint/types": "^8.0.0", "@typescript-eslint/utils": "^8.0.0" }, @@ -7391,16 +7401,16 @@ } }, "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.0.tgz", + "integrity": "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g==", "dev": true, "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" + "environment": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7657,6 +7667,15 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.3.tgz", + "integrity": "sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -7728,6 +7747,19 @@ "node": ">=18" } }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/bonjour-service": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", @@ -7770,9 +7802,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", - "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.0.tgz", + "integrity": "sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==", "funding": [ { "type": "opencollective", @@ -7789,9 +7821,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001737", - "electron-to-chromium": "^1.5.211", - "node-releases": "^2.0.19", + "baseline-browser-mapping": "^2.8.2", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { @@ -8859,6 +8892,19 @@ "node": ">= 10" } }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/d3-ease": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", @@ -9174,9 +9220,9 @@ "optional": true }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -9279,9 +9325,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -9418,9 +9464,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.217", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.217.tgz", - "integrity": "sha512-Pludfu5iBxp9XzNl0qq2G87hdD17ZV7h5T4n6rQXDi3nCyloBV3jreE9+8GC6g4X/5yxqVgXEURpcLtM0WS4jA==", + "version": "1.5.218", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.218.tgz", + "integrity": "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -9467,6 +9513,20 @@ "iconv-lite": "^0.6.2" } }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -10826,16 +10886,20 @@ } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "devOptional": true, + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/icss-utils": { @@ -11814,22 +11878,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.0.tgz", - "integrity": "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/log-update/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", @@ -11982,9 +12030,9 @@ } }, "node_modules/marked": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.1.tgz", - "integrity": "sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.3.0.tgz", + "integrity": "sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==", "license": "MIT", "peer": true, "bin": { @@ -12554,6 +12602,20 @@ "node": ">= 4.4.x" } }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -12735,9 +12797,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.20.tgz", - "integrity": "sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==", + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", "license": "MIT" }, "node_modules/nopt": { @@ -13907,23 +13969,6 @@ "node": ">= 0.10" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -15013,6 +15058,19 @@ "webpack": "^5.72.1" } }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -15695,19 +15753,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -15776,9 +15821,9 @@ "optional": true }, "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.11.0.tgz", + "integrity": "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 081af6f0..74255f33 100644 --- a/package.json +++ b/package.json @@ -52,9 +52,9 @@ "@angular/cli": "^20.3.1", "@angular/compiler-cli": "~20.3.0", "@angular/language-service": "20.3.0", - "@types/node": "^24.3.1", - "@types/react": "^19.1.12", - "angular-eslint": "20.2.0", + "@types/node": "^24.4.0", + "@types/react": "^19.1.13", + "angular-eslint": "20.3.0", "eslint": "^9.35.0", "ts-node": "^10.9.2", "typescript": "^5.9.2", diff --git a/src/app/auth/services/auth.service.ts b/src/app/auth/services/auth.service.ts index 63efb0ad..57e54fc3 100644 --- a/src/app/auth/services/auth.service.ts +++ b/src/app/auth/services/auth.service.ts @@ -12,7 +12,6 @@ import { ConsoleLogger } from 'src/app/_helpers/console-logger'; export class AuthService { private readonly http = inject(HttpClient); - @Output() logoff = new EventEmitter(); @Output() releaseOldSessions = new EventEmitter(); @@ -69,6 +68,19 @@ export class AuthService { catchError(this.handleError)); } + loginWithSAMLToken(token: string): Observable { + const headers = { 'Authorization': `Bearer ${token}` }; + + return this.http.post(`${environment.apiUrl}/admin/saml/auth`, null, { headers }) + .pipe( + tap(auth => { + this.doLoginUser(auth.admin, auth.jwt); + this.currentTokenSubject.next(auth.jwt.token); + }), + map(() => true), + catchError(this.handleError)); + } + signup(user: { name: string, email: string, password: string, token: string }): Observable { return this.http.post(`${environment.apiUrl}/admin/signup`, user) .pipe( @@ -163,13 +175,13 @@ export class AuthService { } private doLoginUser(user: any, tokens: Tokens) { - const loggegWith = this.getLoggedWith(user); - + const loggedWith = this.getLoggedWith(user); + this.setUserInfo('name', user.name); this.setUserInfo('email', user.email); this.setUserInfo('sessionid', user.id); this.setUserInfo('avatar', user._avatar); - this.setUserInfo('platform', loggegWith, true); + this.setUserInfo('platform', loggedWith, true); this.loggedUser = user.email; this.storeTokens(tokens); @@ -181,9 +193,12 @@ export class AuthService { } private getLoggedWith(user: any): string { - if (user._gitid) return 'GitHub'; - if (user._bitbucketid) return 'Bitbucket'; - return 'Switcher API'; + switch (user.auth_provider) { + case 'github': return 'GitHub'; + case 'bitbucket': return 'Bitbucket'; + case 'saml': return 'SAML'; + default: return 'Switcher API'; + } } private getRefreshToken() { diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html index 1c069321..4914c0a4 100644 --- a/src/app/login/login.component.html +++ b/src/app/login/login.component.html @@ -41,11 +41,18 @@

} @if (hasBitbucketIntegration()) { - } + @if (hasSamlAuthEnabled()) { +
+ + } @if (error) {
{{error}}
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index 505b3ca2..06918a60 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -37,6 +37,8 @@ export class LoginComponent implements OnInit, OnDestroy { this.router.navigate(['/dashboard']); } + this.loginWithSAMLToken(); + this.route.queryParams.subscribe(params => { const platform = params['platform']; const code = params['code']; @@ -45,7 +47,7 @@ export class LoginComponent implements OnInit, OnDestroy { if (platform === 'github') { this.loginWithGitHub(code); } else if (platform === 'bitbucket') { - this.loginWithBitBucket(code); + this.loginWithBitbucket(code); } else { this.router.navigate(['/']); } @@ -63,8 +65,9 @@ export class LoginComponent implements OnInit, OnDestroy { get f() { return this.loginForm.controls; } onSubmit() { - if (this.loginForm.invalid) + if (this.loginForm.invalid) { return; + } this.status = ''; this.loading = true; @@ -84,11 +87,16 @@ export class LoginComponent implements OnInit, OnDestroy { window.location.href = `https://github.com/login/oauth/authorize?client_id=${environment.githubApiClientId}`; } - onBitBucketLogin() { + onBitbucketLogin() { this.loading = true; window.location.href = `https://bitbucket.org/site/oauth2/authorize?client_id=${environment.bitbucketApiClientId}&response_type=code`; } + onSamlLogin() { + this.loading = true; + window.location.href = `${environment.apiUrl}/admin/saml/login`; + } + ngOnDestroy() { this.unsubscribe.next(); this.unsubscribe.complete(); @@ -109,6 +117,10 @@ export class LoginComponent implements OnInit, OnDestroy { hasInternalAuthEnabled(): boolean { return environment.allowInternalAuth; } + + hasSamlAuthEnabled(): boolean { + return environment.allowSamlAuth; + } private isAlive(): void { this.authService.isAlive().pipe(takeUntil(this.unsubscribe)) @@ -136,7 +148,7 @@ export class LoginComponent implements OnInit, OnDestroy { }); } - private loginWithBitBucket(code: string) { + private loginWithBitbucket(code: string) { this.loading = true; this.authService.loginWithBitBucket(code) @@ -147,6 +159,24 @@ export class LoginComponent implements OnInit, OnDestroy { }); } + private loginWithSAMLToken() { + if (window.location.hash) { + const hashParams = new URLSearchParams(window.location.hash.substring(1)); + const token = hashParams.get('token'); + + if (token) { + this.loading = true; + + this.authService.loginWithSAMLToken(token) + .pipe(takeUntil(this.unsubscribe)) + .subscribe({ + next: success => this.onSuccess(success), + error: error => this.onError(error) + }); + } + } + } + private onError(error: any) { ConsoleLogger.printError(error); this.error = error; diff --git a/src/app/settings-module/settings-account/settings-account.component.css b/src/app/settings-module/settings-account/settings-account.component.css index 4303d0d7..bb915196 100644 --- a/src/app/settings-module/settings-account/settings-account.component.css +++ b/src/app/settings-module/settings-account/settings-account.component.css @@ -19,6 +19,8 @@ .avatar { border-radius: 100%; width: 200px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3), 0 6px 20px rgba(0, 0, 0, 0.15); + border: 2px solid rgba(255, 255, 255, 0.1); -webkit-filter: drop-shadow(1px 1px 5px rgba(0,0,0,0.2)); -ms-filter: "progid:DXImageTransform.Microsoft.Dropshadow(OffX=12, OffY=12, Color='#444')"; filter: "progid:DXImageTransform.Microsoft.Dropshadow(OffX=12, OffY=12, Color='#444')"; diff --git a/src/app/settings-module/settings-account/settings-account.component.ts b/src/app/settings-module/settings-account/settings-account.component.ts index 770dfcf7..4c896b88 100644 --- a/src/app/settings-module/settings-account/settings-account.component.ts +++ b/src/app/settings-module/settings-account/settings-account.component.ts @@ -112,15 +112,12 @@ export class SettingsAccountComponent extends BasicComponent implements OnInit, } getPlatformIcon(): string { - if (this.userPlatform === 'Switcher API') { - return "assets\\switcherapi_mark_grey.png"; + switch (this.userPlatform) { + case 'Bitbucket': return "assets\\bitbucket.svg"; + case 'GitHub': return "assets\\github.svg"; + case 'SAML': return "assets\\saml.svg"; + default: return "assets\\switcherapi_mark_grey.png"; } - - if (this.userPlatform === 'GitHub') { - return "assets\\github.svg"; - } - - return "assets\\bitbucket.svg"; } private get accountFormControl() { @@ -131,8 +128,9 @@ export class SettingsAccountComponent extends BasicComponent implements OnInit, this.accountFormControl.name.setValue(this.authService.getUserInfo('name')); this.userEmail = this.authService.getUserInfo('email'); this.userPlatform = this.authService.getUserInfo('platform'); + const avatar = this.authService.getUserInfo('avatar'); - this.profileAvatar = avatar || "assets\\switcherapi_mark_grey.png"; + this.profileAvatar = avatar || "assets\\switcherapi_mark_icon.png"; } private loadDomains(): void { diff --git a/src/app/signup/signup.component.html b/src/app/signup/signup.component.html index 5d09d2d7..630011ce 100644 --- a/src/app/signup/signup.component.html +++ b/src/app/signup/signup.component.html @@ -49,11 +49,18 @@

} @if (hasBitbucketIntegration()) { } + @if (hasSamlAuthEnabled()) { +
+ + } @if (error) {
{{error}}
diff --git a/src/app/signup/signup.component.ts b/src/app/signup/signup.component.ts index c12b652e..219a6058 100644 --- a/src/app/signup/signup.component.ts +++ b/src/app/signup/signup.component.ts @@ -84,11 +84,16 @@ export class SignupComponent implements OnInit, OnDestroy { window.location.href = `https://github.com/login/oauth/authorize?client_id=${environment.githubApiClientId}`; } - onBitBucketLogin() { + onBitbucketLogin() { this.loading = true; window.location.href = `https://bitbucket.org/site/oauth2/authorize?client_id=${environment.bitbucketApiClientId}&response_type=code`; } + onSamlLogin() { + this.loading = true; + window.location.href = `${environment.apiUrl}/admin/saml/login`; + } + ngOnDestroy() { this.unsubscribe.next(); this.unsubscribe.complete(); @@ -110,6 +115,10 @@ export class SignupComponent implements OnInit, OnDestroy { return environment.allowInternalAuth; } + hasSamlAuthEnabled(): boolean { + return environment.allowSamlAuth; + } + private submitForm() { this.authService.signup({ name: this.f.name.value, diff --git a/src/assets/documentation/api.md b/src/assets/documentation/api.md index 80783274..9f620ed4 100644 --- a/src/assets/documentation/api.md +++ b/src/assets/documentation/api.md @@ -14,9 +14,7 @@ Switching fast. Adapt everywhere. ### Auth Providers -Switcher API supports multiple auth providers to sign up and sign in such as email/password, GitHub, and Bitbucket. - -Follow the steps below to set up your OAuth App in GitHub and Bitbucket. +Switcher API supports multiple auth providers such as email/password-based authentication, SAML 2.0 for Single Sign-On (SSO), or GitHub/Bitbucket OAuth. #### GitHub OAuth App setup @@ -49,6 +47,29 @@ Follow the steps below to set up your OAuth App in GitHub and Bitbucket. - BIT_OAUTH_CLIENT_SECRET=your_client_secret 8. Update Switcher Management BITBUCKET_CLIENTID environment variable with your_client_id +#### SSO with SAML 2.0 setup + +1. Obtain the following information from your Identity Provider (IdP): + - Entry Point URL + - X.509 Certificate + - (Optional) Private Key + +2. Update your .env-cmdrc file or ConfigMap/Secret in Kubernetes with the following variables: + - SAML_ENTRY_POINT=your_idp_entry_point_url + - SAML_ISSUER=your_issuer + - SAML_CALLBACK_ENDPOINT_URL=service_provider_callback_endpoint_url + - SAML_REDIRECT_ENDPOINT_URL=web_app_redirect_endpoint_url + - SAML_CERT=your_x509_certificate_base64_encoded + - SAML_PRIVATE_KEY=your_private_key_base64_encoded (if applicable) + - SAML_IDENTIFIER_FORMAT=urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + - SAML_ACCEPTED_CLOCK_SKEW_MS=5000 + - SESSION_SECRET=SESSION_SECRET + +3. Enable SAML authentication in Switcher Management by setting the environment variable SAML_ENABLE=true + +* `service_provider` refers to Switcher API +* `web_app` refers to Switcher Management + ### Running Switcher API from Docker Composer manifest file This option leverages Switcher API and Switcher Management with minimum settings required. diff --git a/src/assets/js/env.js b/src/assets/js/env.js index 500da555..f0dae677 100644 --- a/src/assets/js/env.js +++ b/src/assets/js/env.js @@ -5,6 +5,7 @@ window["env"]["RELEASE_TIME"] = undefined; window["env"]["GOOGLE_RECAPTCHA"] = undefined; window["env"]["ALLOW_INTERNAL_AUTH"] = undefined; + window["env"]["ALLOW_SAML_AUTH"] = undefined; window["env"]["ALLOW_HOME_VIEW"] = undefined; window["env"]["GITHUB_CLIENTID"] = undefined; window["env"]["BITBUCKET_CLIENTID"] = undefined; diff --git a/src/assets/js/env.template.js b/src/assets/js/env.template.js index 8c047d2d..358b5e87 100644 --- a/src/assets/js/env.template.js +++ b/src/assets/js/env.template.js @@ -5,6 +5,7 @@ window["env"]["RELEASE_TIME"] = "${RELEASE_TIME}"; window["env"]["GOOGLE_RECAPTCHA"] = "${GOOGLE_RECAPTCHA}"; window["env"]["ALLOW_INTERNAL_AUTH"] = "${ALLOW_INTERNAL_AUTH}"; + window["env"]["ALLOW_SAML_AUTH"] = "${ALLOW_SAML_AUTH}"; window["env"]["ALLOW_HOME_VIEW"] = "${ALLOW_HOME_VIEW}"; window["env"]["GITHUB_CLIENTID"] = "${GITHUB_CLIENTID}"; window["env"]["BITBUCKET_CLIENTID"] = "${BITBUCKET_CLIENTID}"; diff --git a/src/assets/saml.svg b/src/assets/saml.svg new file mode 100644 index 00000000..1320ade9 --- /dev/null +++ b/src/assets/saml.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/environments/environment.global.ts b/src/environments/environment.global.ts index d66e86fc..068d3295 100644 --- a/src/environments/environment.global.ts +++ b/src/environments/environment.global.ts @@ -3,6 +3,7 @@ export const environment_global = { releaseTime: new Date().toUTCString(), recaptchaPublicKey: '6Lc4fMErAAAAAJSdF2ZRV6HJI_m-nhiDu8Gue18P', allowInternalAuth: true, + allowSamlAuth: false, allowHomeView: false, githubApiClientId: '5745650fe81a1f1f3486', bitbucketApiClientId: 'My5KxNyU3vNxYdP68G', diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index ed154a14..7d7b1f93 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -19,6 +19,7 @@ export const environment = { releaseTime: getEnv("RELEASE_TIME"), recaptchaPublicKey: getEnv("GOOGLE_RECAPTCHA"), allowInternalAuth: getEnv("ALLOW_INTERNAL_AUTH", environment_global.allowInternalAuth), + allowSamlAuth: getEnv("ALLOW_SAML_AUTH", environment_global.allowSamlAuth), allowHomeView: getEnv("ALLOW_HOME_VIEW", environment_global.allowHomeView), githubApiClientId: getEnv("GITHUB_CLIENTID"), bitbucketApiClientId: getEnv("BITBUCKET_CLIENTID"), diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 487e3fd3..11047dca 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -7,6 +7,7 @@ export const environment = { recaptchaPublicKey: environment_global.recaptchaPublicKey, githubApiClientId: environment_global.githubApiClientId, allowInternalAuth: environment_global.allowInternalAuth, + allowSamlAuth: environment_global.allowSamlAuth, allowHomeView: environment_global.allowHomeView, bitbucketApiClientId: environment_global.bitbucketApiClientId, teamInviteLink: environment_global.teamInviteLink,