diff --git a/console/package-lock.json b/console/package-lock.json index 2a0298bb9c..09332abba8 100644 --- a/console/package-lock.json +++ b/console/package-lock.json @@ -8,18 +8,18 @@ "name": "console", "version": "0.0.0", "dependencies": { - "@angular/animations": "~12.2.7", - "@angular/cdk": "~12.2.7", - "@angular/common": "~12.2.7", - "@angular/compiler": "~12.2.7", - "@angular/core": "~12.2.7", - "@angular/forms": "~12.2.7", - "@angular/material": "^12.2.7", - "@angular/material-moment-adapter": "^12.2.7", - "@angular/platform-browser": "~12.2.7", - "@angular/platform-browser-dynamic": "~12.2.7", - "@angular/router": "~12.2.7", - "@angular/service-worker": "~12.2.7", + "@angular/animations": "~12.2.8", + "@angular/cdk": "~12.2.8", + "@angular/common": "~12.2.8", + "@angular/compiler": "~12.2.8", + "@angular/core": "~12.2.8", + "@angular/forms": "~12.2.8", + "@angular/material": "^12.2.8", + "@angular/material-moment-adapter": "^12.2.8", + "@angular/platform-browser": "~12.2.8", + "@angular/platform-browser-dynamic": "~12.2.8", + "@angular/router": "~12.2.8", + "@angular/service-worker": "~12.2.8", "@grpc/grpc-js": "^1.3.2", "@ngx-translate/core": "^13.0.0", "@ngx-translate/http-loader": "^6.0.0", @@ -31,7 +31,7 @@ "cors": "^2.8.5", "file-saver": "^2.0.5", "google-proto-files": "^2.4.0", - "google-protobuf": "^3.17.2", + "google-protobuf": "^3.18.0", "grpc-web": "^1.2.1", "libphonenumber-js": "^1.9.34", "moment": "^2.29.1", @@ -46,13 +46,13 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "~12.2.7", - "@angular/cli": "~12.2.7", - "@angular/compiler-cli": "~12.2.7", - "@angular/language-service": "~12.2.7", - "@types/jasmine": "~3.8.2", + "@angular-devkit/build-angular": "~12.2.8", + "@angular/cli": "~12.2.8", + "@angular/compiler-cli": "~12.2.8", + "@angular/language-service": "~12.2.8", + "@types/jasmine": "~3.9.1", "@types/jasminewd2": "~2.0.10", - "@types/node": "^16.7.6", + "@types/node": "^16.10.2", "codelyzer": "^6.0.0", "jasmine-core": "~3.9.0", "jasmine-spec-reporter": "~7.0.0", @@ -60,12 +60,12 @@ "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", - "karma-jasmine-html-reporter": "^1.5.0", - "prettier": "^2.3.1", + "karma-jasmine-html-reporter": "^1.7.0", + "prettier": "^2.4.1", "protractor": "~7.0.0", "stylelint": "^13.10.0", "stylelint-config-standard": "^22.0.0", - "stylelint-scss": "^3.20.1", + "stylelint-scss": "^3.21.0", "ts-node": "~10.2.1", "tslint": "~6.1.3", "typescript": "^4.2.4" @@ -85,12 +85,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1202.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.7.tgz", - "integrity": "sha512-zqqw3h8jMDYsRrXUNY1J8xtUl6wmBO++yTka+CoEIFetNdLdoWmb5VpaA81i0aSBhXBgnBUUFvqZGdiI7BbV8A==", + "version": "0.1202.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.8.tgz", + "integrity": "sha512-aPzwO3coRIuSjZa8FwFHy2y8OJarXG+afsqOk3muR6anvbdl+Av+m2RT8jjwj5J3D4N2eKZ7ob2q9HDUiHi4Pg==", "dev": true, "dependencies": { - "@angular-devkit/core": "12.2.7", + "@angular-devkit/core": "12.2.8", "rxjs": "6.6.7" }, "engines": { @@ -118,16 +118,16 @@ "dev": true }, "node_modules/@angular-devkit/build-angular": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.7.tgz", - "integrity": "sha512-ZgbdmPEiJ8ShKg6CwNEuot1xCHTC68WfTr1ClUhvMvK9nsBydPdeKAYiqEho8gP4PuC0v3Hssuokfkqdb3Ms/A==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.8.tgz", + "integrity": "sha512-nntuVk7K4DR0cdw1lAFLQKG6CFXQfnA2Ykk48gsMVAW2FHitrjiRfDuBKitx+D7f+cEXAFUO2wymrp9fIT2Z5w==", "dev": true, "dependencies": { "@ampproject/remapping": "1.0.1", - "@angular-devkit/architect": "0.1202.7", - "@angular-devkit/build-optimizer": "0.1202.7", - "@angular-devkit/build-webpack": "0.1202.7", - "@angular-devkit/core": "12.2.7", + "@angular-devkit/architect": "0.1202.8", + "@angular-devkit/build-optimizer": "0.1202.8", + "@angular-devkit/build-webpack": "0.1202.8", + "@angular-devkit/core": "12.2.8", "@babel/core": "7.14.8", "@babel/generator": "7.14.8", "@babel/helper-annotate-as-pure": "7.14.5", @@ -139,7 +139,7 @@ "@babel/template": "7.14.5", "@discoveryjs/json-ext": "0.5.3", "@jsdevtools/coverage-istanbul-loader": "3.0.5", - "@ngtools/webpack": "12.2.7", + "@ngtools/webpack": "12.2.8", "ansi-colors": "4.1.1", "babel-loader": "8.2.2", "browserslist": "^4.9.1", @@ -255,9 +255,9 @@ "dev": true }, "node_modules/@angular-devkit/build-optimizer": { - "version": "0.1202.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.7.tgz", - "integrity": "sha512-/VelwjOjQGZvXLwCuWVJ3MaTb1x0/UKYAqooEUW3yFkv6uXfpCCWywrIBZ3mYrU+m5ZeTjhDY4EFEd2WtBSroA==", + "version": "0.1202.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.8.tgz", + "integrity": "sha512-GyzlbIM5RX5RhnX3wW0YV7K9ctoJQv5O7L/VUuDFpK8yaJjqjC+sZT+rnu6oPGFbPnYMx/BkkxzU2D0z98T4Mg==", "dev": true, "dependencies": { "source-map": "0.7.3", @@ -282,12 +282,12 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1202.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.7.tgz", - "integrity": "sha512-DuWr6jEB/CBlmU1D+n0Jo6BMtYokbpBG0PZtnyzSvcwglIWIhxzFbCC7HTnlEzed+bmCSui7LtlGtkYcpFFsGw==", + "version": "0.1202.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.8.tgz", + "integrity": "sha512-ryzstLoMcJfICToZv/RwqeVNNZ3tn71+S1JxNled469gWnsZAvABfexe8BOaT0b0MTo8h49ULeK1DU8McLY78Q==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1202.7", + "@angular-devkit/architect": "0.1202.8", "rxjs": "6.6.7" }, "engines": { @@ -319,9 +319,9 @@ "dev": true }, "node_modules/@angular-devkit/core": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.7.tgz", - "integrity": "sha512-WeLlDZaudpx10OGDPfVcWu/CaEWiWzAaLTUQz0Ww/yM+01FxR/P8yeH1sYAV1MS6d6KHvXGw7Lpf8PV7IA/zHA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.8.tgz", + "integrity": "sha512-N13N1Lm7qllBXSVZYz4Khw75rnQnS3lu5QiJqlsaNklWgVfVz8jt99AAeGGvNGSLEbmZjlr35YLxu8ugD267Ug==", "dev": true, "dependencies": { "ajv": "8.6.2", @@ -356,12 +356,12 @@ "dev": true }, "node_modules/@angular-devkit/schematics": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.7.tgz", - "integrity": "sha512-E0hCFyyfbixjerf0Okt4ynC6F1dsT2Wl7MwAePe+wzPTHCnKIRTa2PQTxJzdWeTlSkQMkSK6ft2iyWOD/FODng==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.8.tgz", + "integrity": "sha512-SPiMFoCi1TpFXY6h1xGCakgdwT25gGHdbis1MuHE5yMcPLvhl/yr7EQVY1GY00/iMrgeslTHg/UPp4D6xHyQxA==", "dev": true, "dependencies": { - "@angular-devkit/core": "12.2.7", + "@angular-devkit/core": "12.2.8", "ora": "5.4.1", "rxjs": "6.6.7" }, @@ -390,9 +390,9 @@ "dev": true }, "node_modules/@angular/animations": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.7.tgz", - "integrity": "sha512-ehlI4wnlHN213CiQNjYspoT9cEIrtOqVJfsPxUdzOCqCGBajVLxyqHb1skXtfOQXOIhznRS7P/d/4Ht7mWMizg==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.8.tgz", + "integrity": "sha512-uRK3EIIFMMIGBMIZscKt9p+H6lzuGXm+zokj5aZsB4Pu+rz8sc/Uxn26lTzq6o5GApiL4I75inK4Vs5f5RZKnA==", "dependencies": { "tslib": "^2.2.0" }, @@ -400,13 +400,13 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/core": "12.2.7" + "@angular/core": "12.2.8" } }, "node_modules/@angular/cdk": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.7.tgz", - "integrity": "sha512-OMPewaE1VxCSFkLDZQLFMkGQCQEvbizuVRzArZBB5xfxl98YMjntNQjlWH4rGbRhF+eZlpOrC+UAVnzTX2BzAw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.8.tgz", + "integrity": "sha512-M0Y61o0yEVLMg+DSNsaDgiOifAV6OdumTgt2/kNoSuauPRWS0bkZJE58k3LR+cPi1Cho3UXELMKMOXZN9AhofA==", "dependencies": { "tslib": "^2.2.0" }, @@ -420,16 +420,16 @@ } }, "node_modules/@angular/cli": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.7.tgz", - "integrity": "sha512-FH34528+126Cxh/+1cBppBas8tExizKSJgbjpT3zgV6ijwHD7apT5zU9R1TyOhQPd6BhyaURo9Hnsjg49W4bRA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.8.tgz", + "integrity": "sha512-MkTVkQbI+58W1izHKpGdpj0YAFfQnf7oQIyMom13/8l83yuOJdNzFTG/Lt77hx19qaKemEdmqTqdEOOrzaqggw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@angular-devkit/architect": "0.1202.7", - "@angular-devkit/core": "12.2.7", - "@angular-devkit/schematics": "12.2.7", - "@schematics/angular": "12.2.7", + "@angular-devkit/architect": "0.1202.8", + "@angular-devkit/core": "12.2.8", + "@angular-devkit/schematics": "12.2.8", + "@schematics/angular": "12.2.8", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "4.3.2", @@ -456,9 +456,9 @@ } }, "node_modules/@angular/common": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.7.tgz", - "integrity": "sha512-Gug5a59c4NwfmSvO9Ya7DoYjl6ndK7nDuBoPSpp6IHTlNE8FY/BOd29qEp/lYJ4cAWxVk14+lonUPs6C+Szekw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.8.tgz", + "integrity": "sha512-4nFlwC97wNEkB4vU2+xrbzpniuzmw8FG9zfqIeMFLLmceHLR7SQmxVKUrZylNXjT5TXXynpQzrpRAxQ1AEcTSA==", "dependencies": { "tslib": "^2.2.0" }, @@ -466,14 +466,14 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/core": "12.2.7", + "@angular/core": "12.2.8", "rxjs": "^6.5.3 || ^7.0.0" } }, "node_modules/@angular/compiler": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.7.tgz", - "integrity": "sha512-9X7Vrfg6dWfYKPAJLQYR5W7N8WnESg8PG07gNzHZtavETPrDIoX+Av/kQcEdPu14zTZE5NWx5u5TUByFgouQiQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.8.tgz", + "integrity": "sha512-11AswnrVeipKibK0Ra4n3TjZfr6QYpiNGPvT6XQB9NLgSthAPTa9T1Bige2yQhgyWNaZsOPko/jhhNS8ufSZCg==", "dependencies": { "tslib": "^2.2.0" }, @@ -482,9 +482,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.7.tgz", - "integrity": "sha512-otsy3t3psrEWNbnjADaZVhBGBmBmBGxqknNoJ1+UeqSWf4z7su736jyzerxD684vmk08U6X2loxOuDr90idjPA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.8.tgz", + "integrity": "sha512-KFGYsDATUCEoY4KEVcpxyT5S0k2T14hIlmBxzmzRhsCVi2ZLti5E2tnqQi2klKCFxmsk7VYd3kqWRJWBZieQtA==", "dev": true, "dependencies": { "@babel/core": "^7.8.6", @@ -512,7 +512,7 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/compiler": "12.2.7", + "@angular/compiler": "12.2.8", "typescript": ">=4.2.3 <4.4" } }, @@ -544,9 +544,9 @@ } }, "node_modules/@angular/core": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.7.tgz", - "integrity": "sha512-no4mQ4O1euNH6odho1H27dcUmYBaNuyYvpPvv0wbb1pMT3Mm2J/uueePx/fvwg3nQ+vnk/yL1VCCqR7Mt62nHA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.8.tgz", + "integrity": "sha512-ko7RJ8BImcMiI64Z8DM54ylkUwu2r/Mhf37BME0EEm+RIrH0KUVzrFOl2rMaxKBZUtY9qaxvVt43bZPrvN2acg==", "dependencies": { "tslib": "^2.2.0" }, @@ -559,9 +559,9 @@ } }, "node_modules/@angular/forms": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.7.tgz", - "integrity": "sha512-TtXnwE/bouEtGddaaSGytwCoyRN8YPNN/yf81fFM9LOGef4ZpABMtuMnsZxlDS+91AGpVSzvR511O5DG1BXc4Q==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.8.tgz", + "integrity": "sha512-/4w+ggQdUg9Ab5s2yhd9A06uevRsyoZ28vGgLU861PZUe5hR4+Gv3XdIuyEBOFvifanWzuxR0xL3okVZAKCXyA==", "dependencies": { "tslib": "^2.2.0" }, @@ -569,31 +569,31 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/common": "12.2.7", - "@angular/core": "12.2.7", - "@angular/platform-browser": "12.2.7", + "@angular/common": "12.2.8", + "@angular/core": "12.2.8", + "@angular/platform-browser": "12.2.8", "rxjs": "^6.5.3 || ^7.0.0" } }, "node_modules/@angular/language-service": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-12.2.7.tgz", - "integrity": "sha512-LCr4CUL1IFLfW9oHpv43/lhJ/kzCP4vhsGTmtOPup7Oc8/lVGUxvFrnIPx0o9qgSjT/ATbWr29+QY0bk02gsRQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-12.2.8.tgz", + "integrity": "sha512-/+GumfwB/j1ILrA5a8iv22FK+0M0GirnHjfOnqiRlcWPlmBkWSr+8RhjjT9TwcZsPS6QFxOBQUj1I5SDwG47Ug==", "dev": true, "engines": { "node": "^12.14.1 || >=14.0.0" } }, "node_modules/@angular/material": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-12.2.7.tgz", - "integrity": "sha512-uaenRwRywvn0hMvRIsSaFIgUSrfHHZMr/uAC3uDZh7zAVmsbAqgzyrNo7i4L5vZxQa3q1EXn1fFPPOTrPfqdJw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-12.2.8.tgz", + "integrity": "sha512-wRTaTZIGC9+2e8aft44V9Qqwp3PsR9AG0FeJ0spl8mdOlYEqMMyoRXjvMiWIjo2ywxHLoQgLXXsWn3ip2xnnVg==", "dependencies": { "tslib": "^2.2.0" }, "peerDependencies": { "@angular/animations": "^12.0.0 || ^13.0.0-0", - "@angular/cdk": "12.2.7", + "@angular/cdk": "12.2.8", "@angular/common": "^12.0.0 || ^13.0.0-0", "@angular/core": "^12.0.0 || ^13.0.0-0", "@angular/forms": "^12.0.0 || ^13.0.0-0", @@ -601,22 +601,22 @@ } }, "node_modules/@angular/material-moment-adapter": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-12.2.7.tgz", - "integrity": "sha512-nwDTQLP5al5LJ+Sl4fJBnesfRjzaLe4ZJZOy+Yl5F+UU/JJSOZuYs38KqTYb89SJDnWnGsx6SFk9eqjQjGfpXQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-12.2.8.tgz", + "integrity": "sha512-uk81sYrY4TfX889ZXo7l7iV8Idxww+sTHzZueq8xC9UTG35FM0KLSvUa/EqwR31lau+6rbgKFCGurQVhBvHrQA==", "dependencies": { "tslib": "^2.2.0" }, "peerDependencies": { "@angular/core": "^12.0.0 || ^13.0.0-0", - "@angular/material": "12.2.7", + "@angular/material": "12.2.8", "moment": "^2.18.1" } }, "node_modules/@angular/platform-browser": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.7.tgz", - "integrity": "sha512-mCQ5KqskMb98DLowyKfixH+ZpTjs5WuIZw9BqPc2knOlUxmfTuDRf5xDQn9Nur2ASF1wfJpaOogW685nB3ojnQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.8.tgz", + "integrity": "sha512-U79tj/fOIKYQbtrRhZm6IcHilUt3UEmloRh7hn2ozhYWOgJmTpygR6FIvvu1X7urAFoOMGY25UZjBNxbnabRNw==", "dependencies": { "tslib": "^2.2.0" }, @@ -624,9 +624,9 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/animations": "12.2.7", - "@angular/common": "12.2.7", - "@angular/core": "12.2.7" + "@angular/animations": "12.2.8", + "@angular/common": "12.2.8", + "@angular/core": "12.2.8" }, "peerDependenciesMeta": { "@angular/animations": { @@ -635,9 +635,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.7.tgz", - "integrity": "sha512-KxIotZR/NaM4r6OyjVxpPIg2AOk2jXpZy77g868tzqt8GQVJ6NXHoNTIAfQhEelr6bSIELm+mTqhDbNNIrXEnQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.8.tgz", + "integrity": "sha512-kHU4mbbrc1TW5Fz9OHRN5IQcFsmEm3zR5g5V1QTFoLjj6jnRao2xd5KZ8Owt0vcf+Qr4/v4kQGh2pIFZJFMaxQ==", "dependencies": { "tslib": "^2.2.0" }, @@ -645,16 +645,16 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/common": "12.2.7", - "@angular/compiler": "12.2.7", - "@angular/core": "12.2.7", - "@angular/platform-browser": "12.2.7" + "@angular/common": "12.2.8", + "@angular/compiler": "12.2.8", + "@angular/core": "12.2.8", + "@angular/platform-browser": "12.2.8" } }, "node_modules/@angular/router": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.7.tgz", - "integrity": "sha512-3RzeBXbV0B+sdSskRYV07KsCgcS8dMmce6oUQrDskEnAmakzFo+R6OVKBFhPtTrqUstHVUsXr2kcoaaPVLquYw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.8.tgz", + "integrity": "sha512-uYPT968IoGH01gUxHTG86hkFIS6G/sWr7QFXhuIzQHTnb+OeyaqWiw4iLc/QDAEPHJ7Wz6gNvCVHv9R2yod00g==", "dependencies": { "tslib": "^2.2.0" }, @@ -662,16 +662,16 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/common": "12.2.7", - "@angular/core": "12.2.7", - "@angular/platform-browser": "12.2.7", + "@angular/common": "12.2.8", + "@angular/core": "12.2.8", + "@angular/platform-browser": "12.2.8", "rxjs": "^6.5.3 || ^7.0.0" } }, "node_modules/@angular/service-worker": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-12.2.7.tgz", - "integrity": "sha512-0Lz2y/1l2/mOmpuKVmzEFPOC+Gnj8LHJ6w/2DThXQdFxueYInBd+zF9i65TeVtytWe0ZtGEcLXX5GymaQhxsSg==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-12.2.8.tgz", + "integrity": "sha512-7/A6gSHrcUa7XHXp3WezUwqsMR4y8To1AvoZ7u0IGC/hbf9CpFk/RnMsItr8h4mxLQz7z214euEUAehYL9RunA==", "dependencies": { "tslib": "^2.2.0" }, @@ -682,8 +682,8 @@ "node": "^12.14.1 || >=14.0.0" }, "peerDependencies": { - "@angular/common": "12.2.7", - "@angular/core": "12.2.7" + "@angular/common": "12.2.8", + "@angular/core": "12.2.8" } }, "node_modules/@assemblyscript/loader": { @@ -2411,9 +2411,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.7.tgz", - "integrity": "sha512-RX5UQA9Bwp/J5GPGtJiwEOQUdf/0UqdeIZtktOZJ4x3K676l//PCFxxxgGqi2qUR2eu/wLAyiDhvDwqDixsngQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.8.tgz", + "integrity": "sha512-utQrOXm4eS3BEQh2/y8zt5fVF9cuakfVQSPD/pWPKLqZsGc9wB3CHkrQED0EizsBfrsn5yLc3yPh3P8yGUmdRw==", "dev": true, "engines": { "node": "^12.14.1 || >=14.0.0", @@ -2629,13 +2629,13 @@ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, "node_modules/@schematics/angular": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.7.tgz", - "integrity": "sha512-wGqp0jC545Fwf0ydBkeoJHx9snFW+uqn40WwVqs/27Nh4AEHB5uzwzLY7Ykae95Zn802a61KPqSNWpez2fWWGA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.8.tgz", + "integrity": "sha512-xkVcX6lTHC5JzDOjGdRAZutVVpxkRkT84vXtVlJwojyhNjAZg5dm/GC84+gVGfmVnO9vkUIYo/vGoN+/ydcSdA==", "dev": true, "dependencies": { - "@angular-devkit/core": "12.2.7", - "@angular-devkit/schematics": "12.2.7", + "@angular-devkit/core": "12.2.8", + "@angular-devkit/schematics": "12.2.8", "jsonc-parser": "3.0.0" }, "engines": { @@ -2778,9 +2778,9 @@ "integrity": "sha512-MDpu7lit927cdLtBzTPUFjXGANFUnu5ThPqjygY8XmCyI/oDlIA0jAi4sffGOxYaLK2CCxAuU9wGxsgAQbA6FQ==" }, "node_modules/@types/jasmine": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.8.2.tgz", - "integrity": "sha512-u5h7dqzy2XpXTzhOzSNQUQpKGFvROF8ElNX9P/TJvsHnTg/JvsAseVsGWQAQQldqanYaM+5kwxW909BBFAUYsg==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.9.1.tgz", + "integrity": "sha512-PVpjh8S8lqKFKurWSKdFATlfBHGPzgy0PoDdzQ+rr78jTQ0aacyh9YndzZcAUPxhk4kRujItFFGQdUJ7flHumw==", "dev": true }, "node_modules/@types/jasminewd2": { @@ -2825,9 +2825,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.6.tgz", - "integrity": "sha512-VESVNFoa/ahYA62xnLBjo5ur6gPsgEE5cNRy8SrdnkZ2nwJSW0kJ4ufbFr2zuU9ALtHM8juY53VcRoTA7htXSg==" + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", + "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.0", @@ -7458,9 +7458,9 @@ } }, "node_modules/google-protobuf": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz", - "integrity": "sha512-OVPzcSWIAJ+d5yiHyeaLrdufQtrvaBrF4JQg+z8ynTkbO3uFcujqXszTumqg1cGsAsjkWnI+M5B1xZ19yR4Wyg==" + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.18.0.tgz", + "integrity": "sha512-WlaQWRkUOo/lm9uTgNH6nk9IQt814RggWPzKBfnAVewOFzSzRUSmS1yUWRT6ixW1vS7er5p6tmLSmwzpPpmc8A==" }, "node_modules/graceful-fs": { "version": "4.2.6", @@ -9341,12 +9341,12 @@ } }, "node_modules/karma-jasmine-html-reporter": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.6.0.tgz", - "integrity": "sha512-ELO9yf0cNqpzaNLsfFgXd/wxZVYkE2+ECUwhMHUD4PZ17kcsPsYsVyjquiRqyMn2jkd2sHt0IeMyAyq1MC23Fw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", + "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", "dev": true, "peerDependencies": { - "jasmine-core": ">=3.7.1", + "jasmine-core": ">=3.8", "karma": ">=0.9", "karma-jasmine": ">=1.1" } @@ -14174,9 +14174,9 @@ } }, "node_modules/prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -16895,9 +16895,9 @@ } }, "node_modules/stylelint-scss": { - "version": "3.20.1", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.20.1.tgz", - "integrity": "sha512-OTd55O1TTAC5nGKkVmUDLpz53LlK39R3MImv1CfuvsK7/qugktqiZAeQLuuC4UBhzxCnsc7fp9u/gfRZwFAIkA==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.21.0.tgz", + "integrity": "sha512-CMI2wSHL+XVlNExpauy/+DbUcB/oUZLARDtMIXkpV/5yd8nthzylYd1cdHeDMJVBXeYHldsnebUX6MoV5zPW4A==", "dev": true, "dependencies": { "lodash": "^4.17.15", @@ -19718,12 +19718,12 @@ } }, "@angular-devkit/architect": { - "version": "0.1202.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.7.tgz", - "integrity": "sha512-zqqw3h8jMDYsRrXUNY1J8xtUl6wmBO++yTka+CoEIFetNdLdoWmb5VpaA81i0aSBhXBgnBUUFvqZGdiI7BbV8A==", + "version": "0.1202.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1202.8.tgz", + "integrity": "sha512-aPzwO3coRIuSjZa8FwFHy2y8OJarXG+afsqOk3muR6anvbdl+Av+m2RT8jjwj5J3D4N2eKZ7ob2q9HDUiHi4Pg==", "dev": true, "requires": { - "@angular-devkit/core": "12.2.7", + "@angular-devkit/core": "12.2.8", "rxjs": "6.6.7" }, "dependencies": { @@ -19745,16 +19745,16 @@ } }, "@angular-devkit/build-angular": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.7.tgz", - "integrity": "sha512-ZgbdmPEiJ8ShKg6CwNEuot1xCHTC68WfTr1ClUhvMvK9nsBydPdeKAYiqEho8gP4PuC0v3Hssuokfkqdb3Ms/A==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.2.8.tgz", + "integrity": "sha512-nntuVk7K4DR0cdw1lAFLQKG6CFXQfnA2Ykk48gsMVAW2FHitrjiRfDuBKitx+D7f+cEXAFUO2wymrp9fIT2Z5w==", "dev": true, "requires": { "@ampproject/remapping": "1.0.1", - "@angular-devkit/architect": "0.1202.7", - "@angular-devkit/build-optimizer": "0.1202.7", - "@angular-devkit/build-webpack": "0.1202.7", - "@angular-devkit/core": "12.2.7", + "@angular-devkit/architect": "0.1202.8", + "@angular-devkit/build-optimizer": "0.1202.8", + "@angular-devkit/build-webpack": "0.1202.8", + "@angular-devkit/core": "12.2.8", "@babel/core": "7.14.8", "@babel/generator": "7.14.8", "@babel/helper-annotate-as-pure": "7.14.5", @@ -19766,7 +19766,7 @@ "@babel/template": "7.14.5", "@discoveryjs/json-ext": "0.5.3", "@jsdevtools/coverage-istanbul-loader": "3.0.5", - "@ngtools/webpack": "12.2.7", + "@ngtools/webpack": "12.2.8", "ansi-colors": "4.1.1", "babel-loader": "8.2.2", "browserslist": "^4.9.1", @@ -19842,9 +19842,9 @@ } }, "@angular-devkit/build-optimizer": { - "version": "0.1202.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.7.tgz", - "integrity": "sha512-/VelwjOjQGZvXLwCuWVJ3MaTb1x0/UKYAqooEUW3yFkv6uXfpCCWywrIBZ3mYrU+m5ZeTjhDY4EFEd2WtBSroA==", + "version": "0.1202.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1202.8.tgz", + "integrity": "sha512-GyzlbIM5RX5RhnX3wW0YV7K9ctoJQv5O7L/VUuDFpK8yaJjqjC+sZT+rnu6oPGFbPnYMx/BkkxzU2D0z98T4Mg==", "dev": true, "requires": { "source-map": "0.7.3", @@ -19853,12 +19853,12 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.1202.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.7.tgz", - "integrity": "sha512-DuWr6jEB/CBlmU1D+n0Jo6BMtYokbpBG0PZtnyzSvcwglIWIhxzFbCC7HTnlEzed+bmCSui7LtlGtkYcpFFsGw==", + "version": "0.1202.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1202.8.tgz", + "integrity": "sha512-ryzstLoMcJfICToZv/RwqeVNNZ3tn71+S1JxNled469gWnsZAvABfexe8BOaT0b0MTo8h49ULeK1DU8McLY78Q==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1202.7", + "@angular-devkit/architect": "0.1202.8", "rxjs": "6.6.7" }, "dependencies": { @@ -19880,9 +19880,9 @@ } }, "@angular-devkit/core": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.7.tgz", - "integrity": "sha512-WeLlDZaudpx10OGDPfVcWu/CaEWiWzAaLTUQz0Ww/yM+01FxR/P8yeH1sYAV1MS6d6KHvXGw7Lpf8PV7IA/zHA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.2.8.tgz", + "integrity": "sha512-N13N1Lm7qllBXSVZYz4Khw75rnQnS3lu5QiJqlsaNklWgVfVz8jt99AAeGGvNGSLEbmZjlr35YLxu8ugD267Ug==", "dev": true, "requires": { "ajv": "8.6.2", @@ -19911,12 +19911,12 @@ } }, "@angular-devkit/schematics": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.7.tgz", - "integrity": "sha512-E0hCFyyfbixjerf0Okt4ynC6F1dsT2Wl7MwAePe+wzPTHCnKIRTa2PQTxJzdWeTlSkQMkSK6ft2iyWOD/FODng==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.8.tgz", + "integrity": "sha512-SPiMFoCi1TpFXY6h1xGCakgdwT25gGHdbis1MuHE5yMcPLvhl/yr7EQVY1GY00/iMrgeslTHg/UPp4D6xHyQxA==", "dev": true, "requires": { - "@angular-devkit/core": "12.2.7", + "@angular-devkit/core": "12.2.8", "ora": "5.4.1", "rxjs": "6.6.7" }, @@ -19939,32 +19939,32 @@ } }, "@angular/animations": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.7.tgz", - "integrity": "sha512-ehlI4wnlHN213CiQNjYspoT9cEIrtOqVJfsPxUdzOCqCGBajVLxyqHb1skXtfOQXOIhznRS7P/d/4Ht7mWMizg==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.2.8.tgz", + "integrity": "sha512-uRK3EIIFMMIGBMIZscKt9p+H6lzuGXm+zokj5aZsB4Pu+rz8sc/Uxn26lTzq6o5GApiL4I75inK4Vs5f5RZKnA==", "requires": { "tslib": "^2.2.0" } }, "@angular/cdk": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.7.tgz", - "integrity": "sha512-OMPewaE1VxCSFkLDZQLFMkGQCQEvbizuVRzArZBB5xfxl98YMjntNQjlWH4rGbRhF+eZlpOrC+UAVnzTX2BzAw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.8.tgz", + "integrity": "sha512-M0Y61o0yEVLMg+DSNsaDgiOifAV6OdumTgt2/kNoSuauPRWS0bkZJE58k3LR+cPi1Cho3UXELMKMOXZN9AhofA==", "requires": { "parse5": "^5.0.0", "tslib": "^2.2.0" } }, "@angular/cli": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.7.tgz", - "integrity": "sha512-FH34528+126Cxh/+1cBppBas8tExizKSJgbjpT3zgV6ijwHD7apT5zU9R1TyOhQPd6BhyaURo9Hnsjg49W4bRA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.8.tgz", + "integrity": "sha512-MkTVkQbI+58W1izHKpGdpj0YAFfQnf7oQIyMom13/8l83yuOJdNzFTG/Lt77hx19qaKemEdmqTqdEOOrzaqggw==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1202.7", - "@angular-devkit/core": "12.2.7", - "@angular-devkit/schematics": "12.2.7", - "@schematics/angular": "12.2.7", + "@angular-devkit/architect": "0.1202.8", + "@angular-devkit/core": "12.2.8", + "@angular-devkit/schematics": "12.2.8", + "@schematics/angular": "12.2.8", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "4.3.2", @@ -19983,25 +19983,25 @@ } }, "@angular/common": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.7.tgz", - "integrity": "sha512-Gug5a59c4NwfmSvO9Ya7DoYjl6ndK7nDuBoPSpp6IHTlNE8FY/BOd29qEp/lYJ4cAWxVk14+lonUPs6C+Szekw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.2.8.tgz", + "integrity": "sha512-4nFlwC97wNEkB4vU2+xrbzpniuzmw8FG9zfqIeMFLLmceHLR7SQmxVKUrZylNXjT5TXXynpQzrpRAxQ1AEcTSA==", "requires": { "tslib": "^2.2.0" } }, "@angular/compiler": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.7.tgz", - "integrity": "sha512-9X7Vrfg6dWfYKPAJLQYR5W7N8WnESg8PG07gNzHZtavETPrDIoX+Av/kQcEdPu14zTZE5NWx5u5TUByFgouQiQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.2.8.tgz", + "integrity": "sha512-11AswnrVeipKibK0Ra4n3TjZfr6QYpiNGPvT6XQB9NLgSthAPTa9T1Bige2yQhgyWNaZsOPko/jhhNS8ufSZCg==", "requires": { "tslib": "^2.2.0" } }, "@angular/compiler-cli": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.7.tgz", - "integrity": "sha512-otsy3t3psrEWNbnjADaZVhBGBmBmBGxqknNoJ1+UeqSWf4z7su736jyzerxD684vmk08U6X2loxOuDr90idjPA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.8.tgz", + "integrity": "sha512-KFGYsDATUCEoY4KEVcpxyT5S0k2T14hIlmBxzmzRhsCVi2ZLti5E2tnqQi2klKCFxmsk7VYd3kqWRJWBZieQtA==", "dev": true, "requires": { "@babel/core": "^7.8.6", @@ -20044,71 +20044,71 @@ } }, "@angular/core": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.7.tgz", - "integrity": "sha512-no4mQ4O1euNH6odho1H27dcUmYBaNuyYvpPvv0wbb1pMT3Mm2J/uueePx/fvwg3nQ+vnk/yL1VCCqR7Mt62nHA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.2.8.tgz", + "integrity": "sha512-ko7RJ8BImcMiI64Z8DM54ylkUwu2r/Mhf37BME0EEm+RIrH0KUVzrFOl2rMaxKBZUtY9qaxvVt43bZPrvN2acg==", "requires": { "tslib": "^2.2.0" } }, "@angular/forms": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.7.tgz", - "integrity": "sha512-TtXnwE/bouEtGddaaSGytwCoyRN8YPNN/yf81fFM9LOGef4ZpABMtuMnsZxlDS+91AGpVSzvR511O5DG1BXc4Q==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.2.8.tgz", + "integrity": "sha512-/4w+ggQdUg9Ab5s2yhd9A06uevRsyoZ28vGgLU861PZUe5hR4+Gv3XdIuyEBOFvifanWzuxR0xL3okVZAKCXyA==", "requires": { "tslib": "^2.2.0" } }, "@angular/language-service": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-12.2.7.tgz", - "integrity": "sha512-LCr4CUL1IFLfW9oHpv43/lhJ/kzCP4vhsGTmtOPup7Oc8/lVGUxvFrnIPx0o9qgSjT/ATbWr29+QY0bk02gsRQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-12.2.8.tgz", + "integrity": "sha512-/+GumfwB/j1ILrA5a8iv22FK+0M0GirnHjfOnqiRlcWPlmBkWSr+8RhjjT9TwcZsPS6QFxOBQUj1I5SDwG47Ug==", "dev": true }, "@angular/material": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-12.2.7.tgz", - "integrity": "sha512-uaenRwRywvn0hMvRIsSaFIgUSrfHHZMr/uAC3uDZh7zAVmsbAqgzyrNo7i4L5vZxQa3q1EXn1fFPPOTrPfqdJw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-12.2.8.tgz", + "integrity": "sha512-wRTaTZIGC9+2e8aft44V9Qqwp3PsR9AG0FeJ0spl8mdOlYEqMMyoRXjvMiWIjo2ywxHLoQgLXXsWn3ip2xnnVg==", "requires": { "tslib": "^2.2.0" } }, "@angular/material-moment-adapter": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-12.2.7.tgz", - "integrity": "sha512-nwDTQLP5al5LJ+Sl4fJBnesfRjzaLe4ZJZOy+Yl5F+UU/JJSOZuYs38KqTYb89SJDnWnGsx6SFk9eqjQjGfpXQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-12.2.8.tgz", + "integrity": "sha512-uk81sYrY4TfX889ZXo7l7iV8Idxww+sTHzZueq8xC9UTG35FM0KLSvUa/EqwR31lau+6rbgKFCGurQVhBvHrQA==", "requires": { "tslib": "^2.2.0" } }, "@angular/platform-browser": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.7.tgz", - "integrity": "sha512-mCQ5KqskMb98DLowyKfixH+ZpTjs5WuIZw9BqPc2knOlUxmfTuDRf5xDQn9Nur2ASF1wfJpaOogW685nB3ojnQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.8.tgz", + "integrity": "sha512-U79tj/fOIKYQbtrRhZm6IcHilUt3UEmloRh7hn2ozhYWOgJmTpygR6FIvvu1X7urAFoOMGY25UZjBNxbnabRNw==", "requires": { "tslib": "^2.2.0" } }, "@angular/platform-browser-dynamic": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.7.tgz", - "integrity": "sha512-KxIotZR/NaM4r6OyjVxpPIg2AOk2jXpZy77g868tzqt8GQVJ6NXHoNTIAfQhEelr6bSIELm+mTqhDbNNIrXEnQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.2.8.tgz", + "integrity": "sha512-kHU4mbbrc1TW5Fz9OHRN5IQcFsmEm3zR5g5V1QTFoLjj6jnRao2xd5KZ8Owt0vcf+Qr4/v4kQGh2pIFZJFMaxQ==", "requires": { "tslib": "^2.2.0" } }, "@angular/router": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.7.tgz", - "integrity": "sha512-3RzeBXbV0B+sdSskRYV07KsCgcS8dMmce6oUQrDskEnAmakzFo+R6OVKBFhPtTrqUstHVUsXr2kcoaaPVLquYw==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.2.8.tgz", + "integrity": "sha512-uYPT968IoGH01gUxHTG86hkFIS6G/sWr7QFXhuIzQHTnb+OeyaqWiw4iLc/QDAEPHJ7Wz6gNvCVHv9R2yod00g==", "requires": { "tslib": "^2.2.0" } }, "@angular/service-worker": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-12.2.7.tgz", - "integrity": "sha512-0Lz2y/1l2/mOmpuKVmzEFPOC+Gnj8LHJ6w/2DThXQdFxueYInBd+zF9i65TeVtytWe0ZtGEcLXX5GymaQhxsSg==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-12.2.8.tgz", + "integrity": "sha512-7/A6gSHrcUa7XHXp3WezUwqsMR4y8To1AvoZ7u0IGC/hbf9CpFk/RnMsItr8h4mxLQz7z214euEUAehYL9RunA==", "requires": { "tslib": "^2.2.0" } @@ -21329,9 +21329,9 @@ } }, "@ngtools/webpack": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.7.tgz", - "integrity": "sha512-RX5UQA9Bwp/J5GPGtJiwEOQUdf/0UqdeIZtktOZJ4x3K676l//PCFxxxgGqi2qUR2eu/wLAyiDhvDwqDixsngQ==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.8.tgz", + "integrity": "sha512-utQrOXm4eS3BEQh2/y8zt5fVF9cuakfVQSPD/pWPKLqZsGc9wB3CHkrQED0EizsBfrsn5yLc3yPh3P8yGUmdRw==", "dev": true, "requires": {} }, @@ -21507,13 +21507,13 @@ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, "@schematics/angular": { - "version": "12.2.7", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.7.tgz", - "integrity": "sha512-wGqp0jC545Fwf0ydBkeoJHx9snFW+uqn40WwVqs/27Nh4AEHB5uzwzLY7Ykae95Zn802a61KPqSNWpez2fWWGA==", + "version": "12.2.8", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.8.tgz", + "integrity": "sha512-xkVcX6lTHC5JzDOjGdRAZutVVpxkRkT84vXtVlJwojyhNjAZg5dm/GC84+gVGfmVnO9vkUIYo/vGoN+/ydcSdA==", "dev": true, "requires": { - "@angular-devkit/core": "12.2.7", - "@angular-devkit/schematics": "12.2.7", + "@angular-devkit/core": "12.2.8", + "@angular-devkit/schematics": "12.2.8", "jsonc-parser": "3.0.0" } }, @@ -21637,9 +21637,9 @@ "integrity": "sha512-MDpu7lit927cdLtBzTPUFjXGANFUnu5ThPqjygY8XmCyI/oDlIA0jAi4sffGOxYaLK2CCxAuU9wGxsgAQbA6FQ==" }, "@types/jasmine": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.8.2.tgz", - "integrity": "sha512-u5h7dqzy2XpXTzhOzSNQUQpKGFvROF8ElNX9P/TJvsHnTg/JvsAseVsGWQAQQldqanYaM+5kwxW909BBFAUYsg==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.9.1.tgz", + "integrity": "sha512-PVpjh8S8lqKFKurWSKdFATlfBHGPzgy0PoDdzQ+rr78jTQ0aacyh9YndzZcAUPxhk4kRujItFFGQdUJ7flHumw==", "dev": true }, "@types/jasminewd2": { @@ -21684,9 +21684,9 @@ "dev": true }, "@types/node": { - "version": "16.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.6.tgz", - "integrity": "sha512-VESVNFoa/ahYA62xnLBjo5ur6gPsgEE5cNRy8SrdnkZ2nwJSW0kJ4ufbFr2zuU9ALtHM8juY53VcRoTA7htXSg==" + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz", + "integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -25371,9 +25371,9 @@ } }, "google-protobuf": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.17.3.tgz", - "integrity": "sha512-OVPzcSWIAJ+d5yiHyeaLrdufQtrvaBrF4JQg+z8ynTkbO3uFcujqXszTumqg1cGsAsjkWnI+M5B1xZ19yR4Wyg==" + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.18.0.tgz", + "integrity": "sha512-WlaQWRkUOo/lm9uTgNH6nk9IQt814RggWPzKBfnAVewOFzSzRUSmS1yUWRT6ixW1vS7er5p6tmLSmwzpPpmc8A==" }, "graceful-fs": { "version": "4.2.6", @@ -26859,9 +26859,9 @@ } }, "karma-jasmine-html-reporter": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.6.0.tgz", - "integrity": "sha512-ELO9yf0cNqpzaNLsfFgXd/wxZVYkE2+ECUwhMHUD4PZ17kcsPsYsVyjquiRqyMn2jkd2sHt0IeMyAyq1MC23Fw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", + "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", "dev": true, "requires": {} }, @@ -30427,9 +30427,9 @@ } }, "prettier": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", - "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "dev": true }, "pretty-bytes": { @@ -32716,9 +32716,9 @@ } }, "stylelint-scss": { - "version": "3.20.1", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.20.1.tgz", - "integrity": "sha512-OTd55O1TTAC5nGKkVmUDLpz53LlK39R3MImv1CfuvsK7/qugktqiZAeQLuuC4UBhzxCnsc7fp9u/gfRZwFAIkA==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.21.0.tgz", + "integrity": "sha512-CMI2wSHL+XVlNExpauy/+DbUcB/oUZLARDtMIXkpV/5yd8nthzylYd1cdHeDMJVBXeYHldsnebUX6MoV5zPW4A==", "dev": true, "requires": { "lodash": "^4.17.15", diff --git a/console/package.json b/console/package.json index dbba66bf1e..c01316e45c 100644 --- a/console/package.json +++ b/console/package.json @@ -10,18 +10,18 @@ }, "private": true, "dependencies": { - "@angular/animations": "~12.2.7", - "@angular/cdk": "~12.2.7", - "@angular/common": "~12.2.7", - "@angular/compiler": "~12.2.7", - "@angular/core": "~12.2.7", - "@angular/forms": "~12.2.7", - "@angular/material": "^12.2.7", - "@angular/material-moment-adapter": "^12.2.7", - "@angular/platform-browser": "~12.2.7", - "@angular/platform-browser-dynamic": "~12.2.7", - "@angular/router": "~12.2.7", - "@angular/service-worker": "~12.2.7", + "@angular/animations": "~12.2.8", + "@angular/cdk": "~12.2.8", + "@angular/common": "~12.2.8", + "@angular/compiler": "~12.2.8", + "@angular/core": "~12.2.8", + "@angular/forms": "~12.2.8", + "@angular/material": "^12.2.8", + "@angular/material-moment-adapter": "^12.2.8", + "@angular/platform-browser": "~12.2.8", + "@angular/platform-browser-dynamic": "~12.2.8", + "@angular/router": "~12.2.8", + "@angular/service-worker": "~12.2.8", "@grpc/grpc-js": "^1.3.2", "@ngx-translate/core": "^13.0.0", "@ngx-translate/http-loader": "^6.0.0", @@ -33,7 +33,7 @@ "cors": "^2.8.5", "file-saver": "^2.0.5", "google-proto-files": "^2.4.0", - "google-protobuf": "^3.17.2", + "google-protobuf": "^3.18.0", "grpc-web": "^1.2.1", "libphonenumber-js": "^1.9.34", "moment": "^2.29.1", @@ -48,13 +48,13 @@ "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "~12.2.7", - "@angular/cli": "~12.2.7", - "@angular/compiler-cli": "~12.2.7", - "@angular/language-service": "~12.2.7", - "@types/jasmine": "~3.8.2", + "@angular-devkit/build-angular": "~12.2.8", + "@angular/cli": "~12.2.8", + "@angular/compiler-cli": "~12.2.8", + "@angular/language-service": "~12.2.8", + "@types/jasmine": "~3.9.1", "@types/jasminewd2": "~2.0.10", - "@types/node": "^16.7.6", + "@types/node": "^16.10.2", "codelyzer": "^6.0.0", "jasmine-core": "~3.9.0", "jasmine-spec-reporter": "~7.0.0", @@ -62,12 +62,12 @@ "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", - "karma-jasmine-html-reporter": "^1.5.0", - "prettier": "^2.3.1", + "karma-jasmine-html-reporter": "^1.7.0", + "prettier": "^2.4.1", "protractor": "~7.0.0", "stylelint": "^13.10.0", "stylelint-config-standard": "^22.0.0", - "stylelint-scss": "^3.20.1", + "stylelint-scss": "^3.21.0", "ts-node": "~10.2.1", "tslint": "~6.1.3", "typescript": "^4.2.4" diff --git a/console/src/app/app-routing.module.ts b/console/src/app/app-routing.module.ts index 0ed3ae8f64..98065c7290 100644 --- a/console/src/app/app-routing.module.ts +++ b/console/src/app/app-routing.module.ts @@ -26,7 +26,7 @@ const routes: Routes = [ .then(m => m.GrantedProjectsModule), canActivate: [AuthGuard, RoleGuard], data: { - roles: ['project.read'], + roles: ['project.grant.read'], }, }, { diff --git a/console/src/app/app.component.ts b/console/src/app/app.component.ts index bc6d37a535..0326ffdc5a 100644 --- a/console/src/app/app.component.ts +++ b/console/src/app/app.component.ts @@ -10,6 +10,7 @@ import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, from, Observable, of, Subject } from 'rxjs'; import { catchError, debounceTime, finalize, map, take, takeUntil } from 'rxjs/operators'; + import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolbarAnimation } from './animations'; import { TextQueryMethod } from './proto/generated/zitadel/object_pb'; import { Org, OrgNameQuery, OrgQuery } from './proto/generated/zitadel/org_pb'; @@ -159,6 +160,16 @@ export class AppComponent implements OnDestroy { this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/counter.svg'), ); + this.matIconRegistry.addSvgIcon( + 'mdi_openid', + this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/openid.svg'), + ); + + this.matIconRegistry.addSvgIcon( + 'mdi_jwt', + this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/jwt.svg'), + ); + this.matIconRegistry.addSvgIcon( 'mdi_symbol', this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/symbol.svg'), diff --git a/console/src/app/modules/accounts-card/accounts-card.component.html b/console/src/app/modules/accounts-card/accounts-card.component.html index f48d99343a..79cd242af7 100644 --- a/console/src/app/modules/accounts-card/accounts-card.component.html +++ b/console/src/app/modules/accounts-card/accounts-card.component.html @@ -1,42 +1,42 @@
- - + + - {{user.human?.profile?.displayName ? user.human?.profile?.displayName : 'A'}} - {{user.human?.profile?.preferredLoginName}} - IAM USER + {{user.human?.profile?.displayName ? user.human?.profile?.displayName : 'A'}} + {{user?.preferredLoginName}} + IAM USER - -
- - - - + + +
+ {{session.displayName ? session.displayName : session.userName}} + {{session.loginName}} + +
+ + keyboard_arrow_right + + +
+ +
+ + {{'USER.ADDACCOUNT' | translate}} + + + keyboard_arrow_right +
+
- +
\ No newline at end of file diff --git a/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.html b/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.html index 5999665d07..b78687189f 100644 --- a/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.html +++ b/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.html @@ -1,6 +1,6 @@
- +
+ + + + +

{{'IDP.JWT.DESCRIPTION' | translate}}

+ +
+
+ + {{ 'IDP.NAME' | translate }} + + + + {{ 'IDP.JWT.HEADERNAME' | translate }} + + + + {{ 'IDP.ISSUER' | translate }} + + +
+ + +
+

{{'IDP.AUTOREGISTER_DESC' | translate}}

+ + {{'IDP.AUTOREGISTER' | translate}} + +
+
+ +
+ + {{ 'IDP.JWT.JWTENDPOINT' | translate }} + + + + {{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }} + + +
+ +
+ + +
+
+ \ No newline at end of file diff --git a/console/src/app/modules/idp-create/idp-create.component.scss b/console/src/app/modules/idp-create/idp-create.component.scss index 97397c644b..0e04172e42 100644 --- a/console/src/app/modules/idp-create/idp-create.component.scss +++ b/console/src/app/modules/idp-create/idp-create.component.scss @@ -1,3 +1,7 @@ +.desc { + color: var(--grey); + font-size: 14px; +} .container { padding: 4rem 4rem 2rem 4rem; @@ -29,7 +33,6 @@ } .auto-reg-info { - margin: 0 .5rem 1rem .5rem; display: block; width: 100%; @@ -38,7 +41,7 @@ } } -.content { +.idp-content { display: flex; margin: 0 -.5rem; flex-wrap: wrap; @@ -51,7 +54,7 @@ } .formfield { - flex: 1 0 auto; + flex: 1; margin: 0 .5rem; @media only screen and (max-width: 450px) { @@ -60,13 +63,17 @@ } } -.continue-button { - margin-top: 3rem; - display: block; - padding: .5rem 4rem; - - @media only screen and (max-width: 450px) { +.actions { + button[mat-stroked-button] { + float: left; margin-top: 1rem; - margin-bottom: 2rem; + border-radius: .5rem; + } + + button[mat-raised-button] { + float: right; + margin-top: 1rem; + border-radius: .5rem; + min-width: 100px; } } diff --git a/console/src/app/modules/idp-create/idp-create.component.ts b/console/src/app/modules/idp-create/idp-create.component.ts index bffaa4d16b..07672ce961 100644 --- a/console/src/app/modules/idp-create/idp-create.component.ts +++ b/console/src/app/modules/idp-create/idp-create.component.ts @@ -6,14 +6,15 @@ import { MatChipInputEvent } from '@angular/material/chips'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; -import { AddOIDCIDPRequest } from 'src/app/proto/generated/zitadel/admin_pb'; +import { AddJWTIDPRequest, AddOIDCIDPRequest } from 'src/app/proto/generated/zitadel/admin_pb'; import { OIDCMappingField } from 'src/app/proto/generated/zitadel/idp_pb'; -import { AddOrgOIDCIDPRequest } from 'src/app/proto/generated/zitadel/management_pb'; +import { AddOrgJWTIDPRequest, AddOrgOIDCIDPRequest } from 'src/app/proto/generated/zitadel/management_pb'; import { AdminService } from 'src/app/services/admin.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; +import { JWT, OIDC, RadioItemIdpType } from './idptypes'; @Component({ selector: 'app-idp-create', @@ -29,10 +30,23 @@ export class IdpCreateComponent implements OnInit, OnDestroy { private subscription?: Subscription; public projectId: string = ''; - public formGroup!: FormGroup; - public createSteps: number = 1; + public oidcFormGroup!: FormGroup; + public jwtFormGroup!: FormGroup; + + public createSteps: number = 2; public currentCreateStep: number = 1; public loading: boolean = false; + + public idpTypes: RadioItemIdpType[] = [ + OIDC, + JWT, + ]; + + OIDC: any = OIDC; + JWT: any = JWT; + + public idpType!: RadioItemIdpType; + constructor( private router: Router, private route: ActivatedRoute, @@ -40,7 +54,7 @@ export class IdpCreateComponent implements OnInit, OnDestroy { private injector: Injector, private _location: Location, ) { - this.formGroup = new FormGroup({ + this.oidcFormGroup = new FormGroup({ name: new FormControl('', [Validators.required]), clientId: new FormControl('', [Validators.required]), clientSecret: new FormControl('', [Validators.required]), @@ -51,6 +65,16 @@ export class IdpCreateComponent implements OnInit, OnDestroy { autoRegister: new FormControl(false), }); + this.jwtFormGroup = new FormGroup({ + jwtName: new FormControl('', [Validators.required]), + jwtHeaderName: new FormControl('', [Validators.required]), + jwtIssuer: new FormControl('', [Validators.required]), + jwtEndpoint: new FormControl('', [Validators.required]), + jwtKeysEndpoint: new FormControl('', [Validators.required]), + jwtStylingType: new FormControl(0), + jwtAutoRegister: new FormControl(false), + }); + this.route.data.pipe(take(1)).subscribe(data => { this.serviceType = data.serviceType; switch (this.serviceType) { @@ -82,7 +106,7 @@ export class IdpCreateComponent implements OnInit, OnDestroy { this.projectId = projectid; } - public addIdp(): void { + public addOIDCIdp(): void { if (this.serviceType === PolicyComponentServiceType.MGMT) { const req = new AddOrgOIDCIDPRequest(); @@ -133,6 +157,56 @@ export class IdpCreateComponent implements OnInit, OnDestroy { } } + public addJWTIdp(): void { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new AddOrgJWTIDPRequest(); + + req.setName(this.jwtName?.value); + req.setHeaderName(this.jwtHeaderName?.value); + req.setIssuer(this.jwtIssuer?.value); + req.setJwtEndpoint(this.jwtEndpoint?.value); + req.setKeysEndpoint(this.jwtKeysEndpoint?.value); + req.setAutoRegister(this.jwtAutoRegister?.value); + req.setStylingType(this.jwtStylingType?.value); + + this.loading = true; + (this.service as ManagementService).addOrgJWTIDP(req).then((idp) => { + setTimeout(() => { + this.loading = false; + this.router.navigate([ + (this.serviceType === PolicyComponentServiceType.MGMT ? 'org' : + this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam' : ''), + 'policy', 'login']); + }, 2000); + }).catch(error => { + this.toast.showError(error); + }); + } else if (PolicyComponentServiceType.ADMIN) { + const req = new AddJWTIDPRequest(); + + req.setName(this.jwtName?.value); + req.setHeaderName(this.jwtHeaderName?.value); + req.setIssuer(this.jwtIssuer?.value); + req.setJwtEndpoint(this.jwtEndpoint?.value); + req.setKeysEndpoint(this.jwtKeysEndpoint?.value); + req.setAutoRegister(this.jwtAutoRegister?.value); + req.setStylingType(this.jwtStylingType?.value); + + this.loading = true; + (this.service as AdminService).addJWTIDP(req).then((idp) => { + setTimeout(() => { + this.loading = false; + this.router.navigate([ + (this.serviceType === PolicyComponentServiceType.MGMT ? 'org' : + this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam' : ''), + 'policy', 'login']); + }, 2000); + }).catch(error => { + this.toast.showError(error); + }); + } + } + public close(): void { this._location.back(); } @@ -162,35 +236,62 @@ export class IdpCreateComponent implements OnInit, OnDestroy { } public get name(): AbstractControl | null { - return this.formGroup.get('name'); + return this.oidcFormGroup.get('name'); } public get clientId(): AbstractControl | null { - return this.formGroup.get('clientId'); + return this.oidcFormGroup.get('clientId'); } public get clientSecret(): AbstractControl | null { - return this.formGroup.get('clientSecret'); + return this.oidcFormGroup.get('clientSecret'); } public get issuer(): AbstractControl | null { - return this.formGroup.get('issuer'); + return this.oidcFormGroup.get('issuer'); } public get scopesList(): AbstractControl | null { - return this.formGroup.get('scopesList'); + return this.oidcFormGroup.get('scopesList'); } public get autoRegister(): AbstractControl | null { - return this.formGroup.get('autoRegister'); + return this.oidcFormGroup.get('autoRegister'); } public get idpDisplayNameMapping(): AbstractControl | null { - return this.formGroup.get('idpDisplayNameMapping'); + return this.oidcFormGroup.get('idpDisplayNameMapping'); } public get usernameMapping(): AbstractControl | null { - return this.formGroup.get('usernameMapping'); + return this.oidcFormGroup.get('usernameMapping'); } + public get jwtName(): AbstractControl | null { + return this.jwtFormGroup.get('jwtName'); + } + + public get jwtHeaderName(): AbstractControl | null { + return this.jwtFormGroup.get('jwtHeaderName'); + } + + public get jwtIssuer(): AbstractControl | null { + return this.jwtFormGroup.get('jwtIssuer'); + } + + public get jwtEndpoint(): AbstractControl | null { + return this.jwtFormGroup.get('jwtEndpoint'); + } + + public get jwtKeysEndpoint(): AbstractControl | null { + return this.jwtFormGroup.get('jwtKeysEndpoint'); + } + + public get jwtStylingType(): AbstractControl | null { + return this.jwtFormGroup.get('jwtStylingType'); + } + + public get jwtAutoRegister(): AbstractControl | null { + return this.jwtFormGroup.get('jwtAutoRegister'); + } } diff --git a/console/src/app/modules/idp-create/idp-create.module.ts b/console/src/app/modules/idp-create/idp-create.module.ts index e8ff5cdb82..315975a16e 100644 --- a/console/src/app/modules/idp-create/idp-create.module.ts +++ b/console/src/app/modules/idp-create/idp-create.module.ts @@ -14,9 +14,10 @@ import { InputModule } from 'src/app/modules/input/input.module'; import { InfoSectionModule } from '../info-section/info-section.module'; import { IdpCreateRoutingModule } from './idp-create-routing.module'; import { IdpCreateComponent } from './idp-create.component'; +import { IdpTypeRadioComponent } from './idp-type-radio/idp-type-radio.component'; @NgModule({ - declarations: [IdpCreateComponent], + declarations: [IdpCreateComponent, IdpTypeRadioComponent], imports: [ IdpCreateRoutingModule, CommonModule, diff --git a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.html b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.html new file mode 100644 index 0000000000..a6bd703eaa --- /dev/null +++ b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.html @@ -0,0 +1,12 @@ +
+ + + + +
\ No newline at end of file diff --git a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss new file mode 100644 index 0000000000..9d83d54601 --- /dev/null +++ b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss @@ -0,0 +1,60 @@ +@use '~@angular/material' as mat; + +.idp-radio-button-wrapper { + display: flex; + flex-direction: column; + flex-wrap: wrap; + margin: 0 -.5rem; + max-width: 500px; +} + +@mixin idp-type-radio-theme($theme) { + $primary: map-get($theme, primary); + $primary-color: mat.get-color-from-palette($primary, 500); + $primary-color-light: mat.get-color-from-palette($primary, 300); + $primary-color-dark: mat.get-color-from-palette($primary, 700); + $primary-color-contrast: mat.get-color-from-palette($primary, default-contrast); + $is-dark-theme: map-get($theme, is-dark); + $back: map-get($theme, background); + $fg: map-get($theme, foreground); + $cardback: map-get($back, card); + $text: map-get($fg, text); + + input[type="radio"].idp { + appearance: none; + opacity: 0; + display: none; + } + + .cnsl-idp-type-radio-button { + margin: .5rem; + border-radius: .5rem; + display: flex; + flex-direction: column; + cursor: pointer; + position: relative; + box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); + background-color: $cardback; + + .cnsl-idp-type-radio-header { + display: flex; + align-items: center; + padding: 0 1rem; + + span { + margin: 1rem 0; + font-size: 1.1rem; + } + + .fill-space { + flex: 1; + } + } + } + + input.idp:checked + label { + border: 4px solid if($is-dark-theme, $primary-color-dark, $primary-color-light) !important; + background: $primary-color !important; + color: $primary-color-contrast !important; + } +} diff --git a/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.ts b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.ts new file mode 100644 index 0000000000..b876e37da6 --- /dev/null +++ b/console/src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.ts @@ -0,0 +1,18 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +import { OIDC, RadioItemIdpType } from '../idptypes'; + +@Component({ + selector: 'cnsl-idp-type-radio', + templateUrl: './idp-type-radio.component.html', + styleUrls: ['./idp-type-radio.component.scss'], +}) +export class IdpTypeRadioComponent { + @Input() selected: RadioItemIdpType = OIDC; + @Input() types!: RadioItemIdpType[]; + @Output() selectedType: EventEmitter = new EventEmitter(); + + public emitChange(): void { + this.selectedType.emit(this.selected); + } +} diff --git a/console/src/app/modules/idp-create/idptypes.ts b/console/src/app/modules/idp-create/idptypes.ts new file mode 100644 index 0000000000..2271cf8672 --- /dev/null +++ b/console/src/app/modules/idp-create/idptypes.ts @@ -0,0 +1,23 @@ + +export enum IdpCreateType { + OIDC = 'OIDC', + JWT = 'JWT', +} + +export interface RadioItemIdpType { + createType: IdpCreateType; + titleI18nKey: string; + mdi?: string; +} + +export const OIDC = { + titleI18nKey: 'IDP.OIDC.TITLE', + mdi: 'mdi_openid', + createType: IdpCreateType.OIDC, +}; + +export const JWT = { + titleI18nKey: 'IDP.JWT.TITLE', + mdi: 'mdi_jwt', + createType: IdpCreateType.JWT, +}; diff --git a/console/src/app/modules/idp-table/idp-table.component.html b/console/src/app/modules/idp-table/idp-table.component.html index 7fb4e4341f..45fcaaf1a0 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -38,8 +38,14 @@ {{ 'IDP.NAME' | translate }} - {{idp?.name}} - + +
+ {{idp?.name}} + + + +
+
@@ -62,7 +68,7 @@ - {{ 'IDP.CREATIONDATE' | translate }} + {{ 'IDP.DATES' | translate }}
{{ 'IDP.CREATIONDATE' | translate }}: @@ -76,7 +82,7 @@ - {{ 'IDP.TYPE' | translate }} + {{ 'IDP.OWNER' | translate }} {{'IDP.OWNERTYPES.'+idp.owner | translate }} @@ -88,7 +94,7 @@ [disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.providerType == IDPOwnerType.IDP_OWNER_TYPE_ORG" mat-icon-button color="warn" matTooltip="{{'ACTIONS.REMOVE' | translate}}" (click)="removeIdp(idp)"> - remove_circle + diff --git a/console/src/app/modules/idp-table/idp-table.component.scss b/console/src/app/modules/idp-table/idp-table.component.scss index 3673a1759c..ec7eb075a7 100644 --- a/console/src/app/modules/idp-table/idp-table.component.scss +++ b/console/src/app/modules/idp-table/idp-table.component.scss @@ -8,6 +8,8 @@ .table, .paginator { + width: 100% !important; + td, th { padding: 0 1rem; @@ -35,6 +37,15 @@ td { object-fit: contain; margin-top: .5rem; } + + .name-col { + display: flex; + flex-direction: column; + + span { + margin-bottom: .5rem; + } + } } tr { diff --git a/console/src/app/modules/idp-table/idp-table.component.ts b/console/src/app/modules/idp-table/idp-table.component.ts index 62deba4286..7f044059e4 100644 --- a/console/src/app/modules/idp-table/idp-table.component.ts +++ b/console/src/app/modules/idp-table/idp-table.component.ts @@ -6,8 +6,8 @@ import { RouterLink } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { ListIDPsResponse } from 'src/app/proto/generated/zitadel/admin_pb'; -import { IDP, IDPOwnerType, IDPState, IDPStylingType } from 'src/app/proto/generated/zitadel/idp_pb'; -import { ListOrgIDPsResponse } from 'src/app/proto/generated/zitadel/management_pb'; +import { IDP, IDPOwnerType, IDPOwnerTypeQuery, IDPState, IDPStylingType } from 'src/app/proto/generated/zitadel/idp_pb'; +import { IDPQuery, ListOrgIDPsResponse } from 'src/app/proto/generated/zitadel/management_pb'; import { AdminService } from 'src/app/services/admin.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; @@ -17,187 +17,190 @@ import { PolicyComponentServiceType } from '../policies/policy-component-types.e import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; @Component({ - selector: 'app-idp-table', - templateUrl: './idp-table.component.html', - styleUrls: ['./idp-table.component.scss'], + selector: 'app-idp-table', + templateUrl: './idp-table.component.html', + styleUrls: ['./idp-table.component.scss'], }) export class IdpTableComponent implements OnInit { - @Input() public serviceType!: PolicyComponentServiceType; - @Input() service!: AdminService | ManagementService; - @Input() disabled: boolean = false; - @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; - public dataSource: MatTableDataSource - = new MatTableDataSource(); - public selection: SelectionModel - = new SelectionModel(true, []); - public idpResult!: ListIDPsResponse.AsObject | ListOrgIDPsResponse.AsObject; - private loadingSubject: BehaviorSubject = new BehaviorSubject(false); - public loading$: Observable = this.loadingSubject.asObservable(); - public PolicyComponentServiceType: any = PolicyComponentServiceType; - public IDPOwnerType: any = IDPOwnerType; - public IDPState: any = IDPState; - public IDPSTYLINGTYPE: any = IDPStylingType; - @Input() public displayedColumns: string[] = ['select', 'name', 'config', 'dates', 'state']; + @Input() public serviceType!: PolicyComponentServiceType; + @Input() service!: AdminService | ManagementService; + @Input() disabled: boolean = false; + @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; + public dataSource: MatTableDataSource + = new MatTableDataSource(); + public selection: SelectionModel + = new SelectionModel(true, []); + public idpResult!: ListIDPsResponse.AsObject | ListOrgIDPsResponse.AsObject; + private loadingSubject: BehaviorSubject = new BehaviorSubject(false); + public loading$: Observable = this.loadingSubject.asObservable(); + public PolicyComponentServiceType: any = PolicyComponentServiceType; + public IDPOwnerType: any = IDPOwnerType; + public IDPState: any = IDPState; + public IDPSTYLINGTYPE: any = IDPStylingType; + @Input() public displayedColumns: string[] = ['select', 'name', 'dates', 'state']; - @Output() public changedSelection: EventEmitter> - = new EventEmitter(); + @Output() public changedSelection: EventEmitter> + = new EventEmitter(); - constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { - this.selection.changed.subscribe(() => { - this.changedSelection.emit(this.selection.selected); - }); + constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { + this.selection.changed.subscribe(() => { + this.changedSelection.emit(this.selection.selected); + }); + } + + ngOnInit(): void { + this.getData(10, 0); + if (this.serviceType === PolicyComponentServiceType.MGMT) { + this.displayedColumns = ['select', 'name', 'dates', 'state', 'owner']; } - ngOnInit(): void { - this.getData(10, 0); + if (!this.disabled) { + this.displayedColumns.push('actions'); + } + } + + public isAllSelected(): boolean { + const numSelected = this.selection.selected.length; + const numRows = this.dataSource.data.length; + return numSelected === numRows; + } + + public masterToggle(): void { + this.isAllSelected() ? + this.selection.clear() : + this.dataSource.data.forEach(row => this.selection.select(row)); + } + + + public changePage(event: PageEvent): void { + this.getData(event.pageSize, event.pageIndex * event.pageSize); + } + + public deactivateSelectedIdps(): void { + const map: Promise[] = this.selection.selected.map(value => { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + return (this.service as ManagementService).deactivateOrgIDP(value.id); + } else { + return (this.service as AdminService).deactivateIDP(value.id); + } + }); + Promise.all(map).then(() => { + this.selection.clear(); + this.toast.showInfo('IDP.TOAST.SELECTEDDEACTIVATED', true); + this.refreshPage(); + }).catch(error => { + this.toast.showError(error); + }); + } + + public reactivateSelectedIdps(): void { + const map: Promise[] = this.selection.selected.map(value => { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + return (this.service as ManagementService).reactivateOrgIDP(value.id); + } else { + return (this.service as AdminService).reactivateIDP(value.id); + } + }); + Promise.all(map).then(() => { + this.selection.clear(); + this.toast.showInfo('IDP.TOAST.SELECTEDREACTIVATED', true); + this.refreshPage(); + }).catch(error => { + this.toast.showError(error); + }); + } + + public removeIdp(idp: IDP.AsObject): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'IDP.DELETE_TITLE', + descriptionKey: 'IDP.DELETE_DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp) { if (this.serviceType === PolicyComponentServiceType.MGMT) { - this.displayedColumns = ['select', 'name', 'config', 'dates', 'state', 'owner']; - } - - if (!this.disabled) { - this.displayedColumns.push('actions'); - } - } - - public isAllSelected(): boolean { - const numSelected = this.selection.selected.length; - const numRows = this.dataSource.data.length; - return numSelected === numRows; - } - - public masterToggle(): void { - this.isAllSelected() ? - this.selection.clear() : - this.dataSource.data.forEach(row => this.selection.select(row)); - } - - - public changePage(event: PageEvent): void { - this.getData(event.pageSize, event.pageIndex * event.pageSize); - } - - public deactivateSelectedIdps(): void { - const map: Promise[] = this.selection.selected.map(value => { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - return (this.service as ManagementService).deactivateOrgIDP(value.id); - } else { - return (this.service as AdminService).deactivateIDP(value.id); - } - }); - Promise.all(map).then(() => { - this.selection.clear(); - this.toast.showInfo('IDP.TOAST.SELECTEDDEACTIVATED', true); - this.refreshPage(); - }).catch(error => { + (this.service as ManagementService).removeOrgIDP(idp.id).then(() => { + this.toast.showInfo('IDP.TOAST.DELETED', true); + setTimeout(() => { + this.refreshPage(); + }, 1000); + }, error => { this.toast.showError(error); - }); - } - - public reactivateSelectedIdps(): void { - const map: Promise[] = this.selection.selected.map(value => { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - return (this.service as ManagementService).reactivateOrgIDP(value.id); - } else { - return (this.service as AdminService).reactivateIDP(value.id); - } - }); - Promise.all(map).then(() => { - this.selection.clear(); - this.toast.showInfo('IDP.TOAST.SELECTEDREACTIVATED', true); - this.refreshPage(); - }).catch(error => { - this.toast.showError(error); - }); - } - - public removeIdp(idp: IDP.AsObject): void { - const dialogRef = this.dialog.open(WarnDialogComponent, { - data: { - confirmKey: 'ACTIONS.DELETE', - cancelKey: 'ACTIONS.CANCEL', - titleKey: 'IDP.DELETE_TITLE', - descriptionKey: 'IDP.DELETE_DESCRIPTION', - }, - width: '400px', - }); - - dialogRef.afterClosed().subscribe(resp => { - if (resp) { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - (this.service as ManagementService).removeOrgIDP(idp.id).then(() => { - this.toast.showInfo('IDP.TOAST.DELETED', true); - setTimeout(() => { - this.refreshPage(); - }, 1000); - }, error => { - this.toast.showError(error); - }); - } else { - (this.service as AdminService).removeIDP(idp.id).then(() => { - this.toast.showInfo('IDP.TOAST.DELETED', true); - setTimeout(() => { - this.refreshPage(); - }, 1000); - }, error => { - this.toast.showError(error); - }); - } - } - }); - } - - private async getData(limit: number, offset: number): Promise { - this.loadingSubject.next(true); - - if (this.serviceType === PolicyComponentServiceType.MGMT) { - (this.service as ManagementService).listOrgIDPs(limit, offset).then(resp => { - this.idpResult = resp; - this.dataSource.data = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); + }); } else { - (this.service as AdminService).listIDPs(limit, offset).then(resp => { - this.idpResult = resp; - this.dataSource.data = resp.resultList; - - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); + (this.service as AdminService).removeIDP(idp.id).then(() => { + this.toast.showInfo('IDP.TOAST.DELETED', true); + setTimeout(() => { + this.refreshPage(); + }, 1000); + }, error => { + this.toast.showError(error); + }); } + } + }); + } + private async getData(limit: number, offset: number): Promise { + this.loadingSubject.next(true); + + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const query: IDPQuery = new IDPQuery(); + const otQuery: IDPOwnerTypeQuery = new IDPOwnerTypeQuery(); + otQuery.setOwnerType(IDPOwnerType.IDP_OWNER_TYPE_ORG); + query.setOwnerTypeQuery(otQuery); + (this.service as ManagementService).listOrgIDPs(limit, offset, [query]).then(resp => { + this.idpResult = resp; + this.dataSource.data = resp.resultList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } else { + (this.service as AdminService).listIDPs(limit, offset).then(resp => { + this.idpResult = resp; + this.dataSource.data = resp.resultList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); } - public refreshPage(): void { - this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize); - } + } - public get createRouterLink(): RouterLink | any { - if (this.service instanceof AdminService) { - return ['/iam', 'idp', 'create']; - } else if (this.service instanceof ManagementService) { - return ['/org', 'idp', 'create']; - } - } + public refreshPage(): void { + this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize); + } - public routerLinkForRow(row: IDP.AsObject): any { - if (row.id) { - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - switch (row.owner) { - case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM: - return ['/iam', 'idp', row.id]; - case IDPOwnerType.IDP_OWNER_TYPE_ORG: - return ['/org', 'idp', row.id]; - } - break; - case PolicyComponentServiceType.ADMIN: - return ['/iam', 'idp', row.id]; - } - } + public get createRouterLink(): RouterLink | any { + if (this.service instanceof AdminService) { + return ['/iam', 'idp', 'create']; + } else if (this.service instanceof ManagementService) { + return ['/org', 'idp', 'create']; } + } + + public routerLinkForRow(row: IDP.AsObject): any { + if (row.id) { + switch (this.serviceType) { + case PolicyComponentServiceType.MGMT: + switch (row.owner) { + case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM: + return ['/iam', 'idp', row.id]; + case IDPOwnerType.IDP_OWNER_TYPE_ORG: + return ['/org', 'idp', row.id]; + } + break; + case PolicyComponentServiceType.ADMIN: + return ['/iam', 'idp', row.id]; + } + } + } } diff --git a/console/src/app/modules/idp/idp.component.html b/console/src/app/modules/idp/idp.component.html index caaab96d86..27c7ea297e 100644 --- a/console/src/app/modules/idp/idp.component.html +++ b/console/src/app/modules/idp/idp.component.html @@ -1,13 +1,35 @@ - +
+ + + + + + + +
+
-
+ + + -
- - {{ 'IDP.ID' | translate }} - - +
+ {{ 'IDP.NAME' | translate }} @@ -40,77 +62,115 @@
- -

{{'IDP.DETAIL.OIDC.TITLE' | translate}}

-

{{'IDP.DETAIL.OIDC.DESCRIPTION' | translate}}

+ +

{{'IDP.OIDC.TITLE' | translate}}

+

{{'IDP.OIDC.DESCRIPTION' | translate}}

-
- -
- - {{ 'IDP.ISSUER' | translate }} - - - - {{ 'IDP.CLIENTID' | translate }} - - - - Update Client Secret - - - {{ 'IDP.CLIENTSECRET' | translate }} - - -
- - {{ 'IDP.SCOPESLIST' | translate }} + + +
+ + {{ 'IDP.ISSUER' | translate }} + + + + {{ 'IDP.CLIENTID' | translate }} + + + + Update Client Secret + + + {{ 'IDP.CLIENTSECRET' | translate }} + + +
+ + {{ 'IDP.SCOPESLIST' | translate }} - - - -
- - - - {{scope}} cancel - - - - - - {{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }} - - - {{ 'IDP.MAPPINGFIELD.'+field | translate }} - - - - - {{ 'IDP.USERNAMEMAPPING' | translate }} - - - {{ 'IDP.MAPPINGFIELD.'+field | translate }} - - - -
-
- -
- + + +
- + + + + {{scope}} cancel + + + + + + {{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }} + + + {{ 'IDP.MAPPINGFIELD.'+field | translate }} + + + + + {{ 'IDP.USERNAMEMAPPING' | translate }} + + + {{ 'IDP.MAPPINGFIELD.'+field | translate }} + + + +
+ + +
+ +
+ + + +

{{'IDP.JWT.TITLE' | translate}}

+

{{'IDP.JWT.DESCRIPTION' | translate}}

+ +
+ +
+ + {{ 'IDP.ISSUER' | translate }} + + + + + {{ 'IDP.JWT.HEADERNAME' | translate }} + + + + + {{ 'IDP.JWT.JWTENDPOINT' | translate }} + + + + + {{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }} + + +
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/console/src/app/modules/idp/idp.component.scss b/console/src/app/modules/idp/idp.component.scss index c599d021e8..2f9aad3541 100644 --- a/console/src/app/modules/idp/idp.component.scss +++ b/console/src/app/modules/idp/idp.component.scss @@ -5,15 +5,20 @@ @media only screen and (max-width: 450px) { padding: 4rem 1rem 2rem 1rem; } + + .idp-form { + border-top: 1px solid #ffffff20; + padding-top: 1rem; + } } -.content { +.idp-content { display: flex; flex-direction: row; margin: 0 -.5rem; flex-wrap: wrap; - .desc { + .idp-desc { flex-basis: 100%; margin: 0 .5rem; margin-bottom: 1rem; diff --git a/console/src/app/modules/idp/idp.component.ts b/console/src/app/modules/idp/idp.component.ts index 17be806567..74301865d0 100644 --- a/console/src/app/modules/idp/idp.component.ts +++ b/console/src/app/modules/idp/idp.component.ts @@ -3,18 +3,28 @@ import { Location } from '@angular/common'; import { Component, Injector, OnDestroy, Type } from '@angular/core'; import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; import { MatChipInputEvent } from '@angular/material/chips'; -import { ActivatedRoute } from '@angular/router'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subject } from 'rxjs'; import { switchMap, take, takeUntil } from 'rxjs/operators'; -import { UpdateIDPOIDCConfigRequest, UpdateIDPRequest } from 'src/app/proto/generated/zitadel/admin_pb'; -import { IDPStylingType, OIDCMappingField } from 'src/app/proto/generated/zitadel/idp_pb'; -import { UpdateOrgIDPOIDCConfigRequest, UpdateOrgIDPRequest } from 'src/app/proto/generated/zitadel/management_pb'; +import { + UpdateIDPJWTConfigRequest, + UpdateIDPOIDCConfigRequest, + UpdateIDPRequest, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { IDP, IDPState, IDPStylingType, OIDCMappingField } from 'src/app/proto/generated/zitadel/idp_pb'; +import { + UpdateOrgIDPJWTConfigRequest, + UpdateOrgIDPOIDCConfigRequest, + UpdateOrgIDPRequest, +} from 'src/app/proto/generated/zitadel/management_pb'; import { AdminService } from 'src/app/services/admin.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; +import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; @Component({ selector: 'app-idp', @@ -28,13 +38,18 @@ export class IdpComponent implements OnDestroy { public showIdSecretSection: boolean = false; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; private service!: ManagementService | AdminService; + public PolicyComponentServiceType: any = PolicyComponentServiceType; public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; + public idp!: IDP.AsObject; private destroy$: Subject = new Subject(); public projectId: string = ''; public idpForm!: FormGroup; public oidcConfigForm!: FormGroup; + public jwtConfigForm!: FormGroup; + + IDPState: any = IDPState; public canWrite: Observable = this.authService.isAllowed([this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : this.serviceType === PolicyComponentServiceType.MGMT ? @@ -44,8 +59,10 @@ export class IdpComponent implements OnDestroy { private toast: ToastService, private injector: Injector, private route: ActivatedRoute, + private router: Router, private _location: Location, private authService: GrpcAuthService, + private dialog: MatDialog, ) { this.idpForm = new FormGroup({ id: new FormControl({ disabled: true, value: '' }, [Validators.required]), @@ -63,6 +80,13 @@ export class IdpComponent implements OnDestroy { usernameMapping: new FormControl(0), }); + this.jwtConfigForm = new FormGroup({ + jwtEndpoint: new FormControl('', [Validators.required]), + issuer: new FormControl('', [Validators.required]), + keysEndpoint: new FormControl('', [Validators.required]), + headerName: new FormControl('', [Validators.required]), + }); + this.route.data.pipe( takeUntil(this.destroy$), switchMap(data => { @@ -95,20 +119,26 @@ export class IdpComponent implements OnDestroy { (this.service as ManagementService).getOrgIDPByID(id).then(resp => { if (resp.idp) { - const idpObject = resp.idp; - this.idpForm.patchValue(idpObject); - if (idpObject.oidcConfig) { - this.oidcConfigForm.patchValue(idpObject.oidcConfig); + this.idp = resp.idp; + this.idpForm.patchValue(this.idp); + if (this.idp.oidcConfig) { + this.oidcConfigForm.patchValue(this.idp.oidcConfig); + } else if (this.idp.jwtConfig) { + this.jwtConfigForm.patchValue(this.idp.jwtConfig); + this.jwtIssuer?.setValue(this.idp.jwtConfig.issuer); } } }); } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { (this.service as AdminService).getIDPByID(id).then(resp => { if (resp.idp) { - const idpObject = resp.idp; - this.idpForm.patchValue(idpObject); - if (idpObject.oidcConfig) { - this.oidcConfigForm.patchValue(idpObject.oidcConfig); + this.idp = resp.idp; + this.idpForm.patchValue(this.idp); + if (this.idp.oidcConfig) { + this.oidcConfigForm.patchValue(this.idp.oidcConfig); + } else if (this.idp.jwtConfig) { + this.jwtConfigForm.patchValue(this.idp.jwtConfig); + this.jwtIssuer?.setValue(this.idp.jwtConfig.issuer); } } }); @@ -122,11 +152,9 @@ export class IdpComponent implements OnDestroy { if (canWrite) { this.idpForm.enable(); this.oidcConfigForm.enable(); - this.id?.disable(); } else { this.idpForm.disable(); this.oidcConfigForm.disable(); - this.id?.disable(); } }); } @@ -136,32 +164,98 @@ export class IdpComponent implements OnDestroy { this.destroy$.complete(); } + public deleteIdp(): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'IDP.DELETE_TITLE', + descriptionKey: 'IDP.DELETE_DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + (this.service as ManagementService).removeOrgIDP(this.idp.id).then(() => { + this.toast.showInfo('IDP.TOAST.DELETED', true); + this.router.navigate(this.backroutes); + }).catch((error: any) => { + this.toast.showError(error); + }); + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + (this.service as AdminService).removeIDP(this.idp.id).then(() => { + this.toast.showInfo('IDP.TOAST.DELETED', true); + this.router.navigate(this.backroutes); + }).catch((error: any) => { + this.toast.showError(error); + }); + } + } + }); + } + + public changeState(state: IDPState): void { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (state === IDPState.IDP_STATE_ACTIVE) { + (this.service as ManagementService).reactivateOrgIDP(this.idp.id).then(() => { + this.idp.state = state; + this.toast.showInfo('IDP.TOAST.REACTIVATED', true); + }).catch((error: any) => { + this.toast.showError(error); + }); + } else if (state === IDPState.IDP_STATE_INACTIVE) { + (this.service as ManagementService).deactivateOrgIDP(this.idp.id).then(() => { + this.idp.state = state; + this.toast.showInfo('IDP.TOAST.DEACTIVATED', true); + }).catch((error: any) => { + this.toast.showError(error); + }); + } + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + if (state === IDPState.IDP_STATE_ACTIVE) { + (this.service as AdminService).reactivateIDP(this.idp.id).then(() => { + this.idp.state = state; + this.toast.showInfo('IDP.TOAST.REACTIVATED', true); + }).catch((error: any) => { + this.toast.showError(error); + }); + } else if (state === IDPState.IDP_STATE_INACTIVE) { + (this.service as AdminService).deactivateIDP(this.idp.id).then(() => { + this.idp.state = state; + this.toast.showInfo('IDP.TOAST.DEACTIVATED', true); + }).catch((error: any) => { + this.toast.showError(error); + }); + } + } + } + public updateIdp(): void { if (this.serviceType === PolicyComponentServiceType.MGMT) { const req = new UpdateOrgIDPRequest(); - req.setIdpId(this.id?.value); + req.setIdpId(this.idp.id); req.setName(this.name?.value); req.setStylingType(this.stylingType?.value); req.setAutoRegister(this.autoRegister?.value); (this.service as ManagementService).updateOrgIDP(req).then(() => { this.toast.showInfo('IDP.TOAST.SAVED', true); - // this.router.navigate(['idp', ]); }).catch(error => { this.toast.showError(error); }); } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { const req = new UpdateIDPRequest(); - req.setIdpId(this.id?.value); + req.setIdpId(this.idp.id); req.setName(this.name?.value); req.setStylingType(this.stylingType?.value); req.setAutoRegister(this.autoRegister?.value); (this.service as AdminService).updateIDP(req).then(() => { this.toast.showInfo('IDP.TOAST.SAVED', true); - // this.router.navigate(['idp', ]); }).catch(error => { this.toast.showError(error); }); @@ -172,7 +266,7 @@ export class IdpComponent implements OnDestroy { if (this.serviceType === PolicyComponentServiceType.MGMT) { const req = new UpdateOrgIDPOIDCConfigRequest(); - req.setIdpId(this.id?.value); + req.setIdpId(this.idp.id); req.setClientId(this.clientId?.value); req.setClientSecret(this.clientSecret?.value); req.setIssuer(this.issuer?.value); @@ -182,14 +276,13 @@ export class IdpComponent implements OnDestroy { (this.service as ManagementService).updateOrgIDPOIDCConfig(req).then((oidcConfig) => { this.toast.showInfo('IDP.TOAST.SAVED', true); - // this.router.navigate(['idp', ]); }).catch(error => { this.toast.showError(error); }); } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { const req = new UpdateIDPOIDCConfigRequest(); - req.setIdpId(this.id?.value); + req.setIdpId(this.idp.id); req.setClientId(this.clientId?.value); req.setClientSecret(this.clientSecret?.value); req.setIssuer(this.issuer?.value); @@ -199,6 +292,39 @@ export class IdpComponent implements OnDestroy { (this.service as AdminService).updateIDPOIDCConfig(req).then((oidcConfig) => { this.toast.showInfo('IDP.TOAST.SAVED', true); + }).catch(error => { + this.toast.showError(error); + }); + } + } + + public updateJwtConfig(): void { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + const req = new UpdateOrgIDPJWTConfigRequest(); + + req.setIdpId(this.idp.id); + req.setIssuer(this.jwtIssuer?.value); + req.setHeaderName(this.headerName?.value); + req.setJwtEndpoint(this.jwtEndpoint?.value); + req.setKeysEndpoint(this.keyEndpoint?.value); + + (this.service as ManagementService).updateOrgIDPJWTConfig(req).then((jwtConfig) => { + this.toast.showInfo('IDP.TOAST.SAVED', true); + // this.router.navigate(['idp', ]); + }).catch(error => { + this.toast.showError(error); + }); + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + const req = new UpdateIDPJWTConfigRequest(); + + req.setIdpId(this.idp.id); + req.setIssuer(this.jwtIssuer?.value); + req.setHeaderName(this.headerName?.value); + req.setJwtEndpoint(this.jwtEndpoint?.value); + req.setKeysEndpoint(this.keyEndpoint?.value); + + (this.service as AdminService).updateIDPJWTConfig(req).then((jwtConfig) => { + this.toast.showInfo('IDP.TOAST.SAVED', true); // this.router.navigate(['idp', ]); }).catch(error => { this.toast.showError(error); @@ -243,10 +369,6 @@ export class IdpComponent implements OnDestroy { } } - public get id(): AbstractControl | null { - return this.idpForm.get('id'); - } - public get name(): AbstractControl | null { return this.idpForm.get('name'); } @@ -282,4 +404,21 @@ export class IdpComponent implements OnDestroy { public get usernameMapping(): AbstractControl | null { return this.oidcConfigForm.get('usernameMapping'); } + + + public get jwtIssuer(): AbstractControl | null { + return this.jwtConfigForm.get('issuer'); + } + + public get jwtEndpoint(): AbstractControl | null { + return this.jwtConfigForm.get('jwtEndpoint'); + } + + public get keyEndpoint(): AbstractControl | null { + return this.jwtConfigForm.get('keysEndpoint'); + } + + public get headerName(): AbstractControl | null { + return this.jwtConfigForm.get('headerName'); + } } diff --git a/console/src/app/modules/idp/idp.module.ts b/console/src/app/modules/idp/idp.module.ts index 0e51572aee..fde45c11e1 100644 --- a/console/src/app/modules/idp/idp.module.ts +++ b/console/src/app/modules/idp/idp.module.ts @@ -5,13 +5,16 @@ import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatChipsModule } from '@angular/material/chips'; import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; import { MatSelectModule } from '@angular/material/select'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module'; import { InputModule } from 'src/app/modules/input/input.module'; +import { InfoRowModule } from '../info-row/info-row.module'; import { InfoSectionModule } from '../info-section/info-section.module'; +import { WarnDialogModule } from '../warn-dialog/warn-dialog.module'; import { IdpRoutingModule } from './idp-routing.module'; import { IdpComponent } from './idp.component'; @@ -24,12 +27,15 @@ import { IdpComponent } from './idp.component'; ReactiveFormsModule, InputModule, MatButtonModule, + WarnDialogModule, MatIconModule, InfoSectionModule, + MatMenuModule, MatTooltipModule, MatSelectModule, TranslateModule, MatCheckboxModule, + InfoRowModule, MatChipsModule, DetailLayoutModule, ], diff --git a/console/src/app/modules/info-row/info-row.component.html b/console/src/app/modules/info-row/info-row.component.html index 51ddb2f0c7..76c054d621 100644 --- a/console/src/app/modules/info-row/info-row.component.html +++ b/console/src/app/modules/info-row/info-row.component.html @@ -79,4 +79,34 @@
- \ No newline at end of file + + +
+
+

{{ 'IDP.ID' | translate }}

+
+ +
+
+ +
+

{{ 'IDP.DETAIL.DATECREATED' | translate }}

+

{{idp?.details?.creationDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}

+
+ +
+

{{ 'IDP.DETAIL.DATECHANGED' | translate }}

+

{{idp?.details?.changeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}

+
+ +
+

{{ 'IDP.STATE' | translate }}

+

{{'IDP.STATES.'+idp.state + | translate}}

+
+
diff --git a/console/src/app/modules/info-row/info-row.component.ts b/console/src/app/modules/info-row/info-row.component.ts index 67e5051df4..474dc3afd3 100644 --- a/console/src/app/modules/info-row/info-row.component.ts +++ b/console/src/app/modules/info-row/info-row.component.ts @@ -1,6 +1,7 @@ import { HttpClient } from '@angular/common/http'; import { Component, Input, OnInit } from '@angular/core'; import { App, AppState } from 'src/app/proto/generated/zitadel/app_pb'; +import { IDP, IDPState } from 'src/app/proto/generated/zitadel/idp_pb'; import { User, UserState } from 'src/app/proto/generated/zitadel/user_pb'; @Component({ @@ -11,8 +12,11 @@ import { User, UserState } from 'src/app/proto/generated/zitadel/user_pb'; export class InfoRowComponent implements OnInit { @Input() public user!: User.AsObject; @Input() public app!: App.AsObject; + @Input() public idp!: IDP.AsObject; + public UserState: any = UserState; public AppState: any = AppState; + public IDPState: any = IDPState; public copied: string = ''; public environmentMap: { [key: string]: string; } = {}; diff --git a/console/src/app/modules/mfa-table/mfa-table.component.html b/console/src/app/modules/mfa-table/mfa-table.component.html deleted file mode 100644 index f8ef5e1a3b..0000000000 --- a/console/src/app/modules/mfa-table/mfa-table.component.html +++ /dev/null @@ -1,16 +0,0 @@ -
- -
-
-
- - {{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': - LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}} -
-
- add -
-
\ No newline at end of file diff --git a/console/src/app/modules/mfa-table/mfa-table.component.scss b/console/src/app/modules/mfa-table/mfa-table.component.scss deleted file mode 100644 index dc481a67d7..0000000000 --- a/console/src/app/modules/mfa-table/mfa-table.component.scss +++ /dev/null @@ -1,62 +0,0 @@ -.t .sp_wrapper { - display: block; -} - -.mfa-list { - display: flex; - flex-wrap: wrap; - margin: 0 -.5rem; - - .mfa { - background-color: #6a506e; - color: white; - } - - .mfa, - .new-mfa { - border: 1px solid var(--grey); - border-radius: .5rem; - display: grid; - align-items: center; - justify-content: center; - margin: .5rem; - padding: 10px; - position: relative; - min-height: 70px; - min-width: 150px; - - .rm { - position: absolute; - display: none; - top: -2px; - left: -2px; - transform: translateX(-50%) translateY(-50%); - cursor: pointer; - color: var(--warn); - transition: all .2s ease; - - &[disabled] { - display: none; - } - } - - &:not(.disabled) { - cursor: default; - - &:hover { - .rm { - display: block; - } - } - } - - &.disabled { - opacity: .5; - cursor: not-allowed; - } - } - - .new-mfa:not(.disabled) { - cursor: pointer; - } -} diff --git a/console/src/app/modules/mfa-table/mfa-table.component.ts b/console/src/app/modules/mfa-table/mfa-table.component.ts deleted file mode 100644 index 2b877b5976..0000000000 --- a/console/src/app/modules/mfa-table/mfa-table.component.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { Component, Input, OnInit, ViewChild } from '@angular/core'; -import { MatDialog } from '@angular/material/dialog'; -import { MatPaginator } from '@angular/material/paginator'; -import { TranslateService } from '@ngx-translate/core'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { - AddMultiFactorToLoginPolicyRequest as AdminAddMultiFactorToLoginPolicyRequest, - AddSecondFactorToLoginPolicyRequest as AdminAddSecondFactorToLoginPolicyRequest, - RemoveMultiFactorFromLoginPolicyRequest as AdminRemoveMultiFactorFromLoginPolicyRequest, - RemoveSecondFactorFromLoginPolicyRequest as AdminRemoveSecondFactorFromLoginPolicyRequest, -} from 'src/app/proto/generated/zitadel/admin_pb'; -import { - AddMultiFactorToLoginPolicyRequest as MgmtAddMultiFactorToLoginPolicyRequest, - AddSecondFactorToLoginPolicyRequest as MgmtAddSecondFactorToLoginPolicyRequest, - RemoveMultiFactorFromLoginPolicyRequest as MgmtRemoveMultiFactorFromLoginPolicyRequest, - RemoveSecondFactorFromLoginPolicyRequest as MgmtRemoveSecondFactorFromLoginPolicyRequest, -} from 'src/app/proto/generated/zitadel/management_pb'; -import { MultiFactorType, SecondFactorType } from 'src/app/proto/generated/zitadel/policy_pb'; -import { AdminService } from 'src/app/services/admin.service'; -import { ManagementService } from 'src/app/services/mgmt.service'; -import { ToastService } from 'src/app/services/toast.service'; - -import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; -import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; -import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component'; - -export enum LoginMethodComponentType { - MultiFactor = 1, - SecondFactor = 2, -} - -@Component({ - selector: 'app-mfa-table', - templateUrl: './mfa-table.component.html', - styleUrls: ['./mfa-table.component.scss'], -}) -export class MfaTableComponent implements OnInit { - public LoginMethodComponentType: any = LoginMethodComponentType; - @Input() componentType!: LoginMethodComponentType; - @Input() public serviceType!: PolicyComponentServiceType; - @Input() service!: AdminService | ManagementService; - @Input() disabled: boolean = false; - @ViewChild(MatPaginator) public paginator!: MatPaginator; - public mfas: Array = []; - - private loadingSubject: BehaviorSubject = new BehaviorSubject(false); - public loading$: Observable = this.loadingSubject.asObservable(); - - public PolicyComponentServiceType: any = PolicyComponentServiceType; - - constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { } - - public ngOnInit(): void { - this.getData(); - } - - public removeMfa(type: MultiFactorType | SecondFactorType): void { - const dialogRef = this.dialog.open(WarnDialogComponent, { - data: { - confirmKey: 'ACTIONS.DELETE', - cancelKey: 'ACTIONS.CANCEL', - titleKey: 'MFA.DELETE.TITLE', - descriptionKey: 'MFA.DELETE.DESCRIPTION', - }, - width: '400px', - }); - - dialogRef.afterClosed().subscribe(resp => { - if (resp) { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - if (this.componentType === LoginMethodComponentType.MultiFactor) { - const req = new MgmtRemoveMultiFactorFromLoginPolicyRequest(); - req.setType(type as MultiFactorType); - (this.service as ManagementService).removeMultiFactorFromLoginPolicy(req).then(() => { - this.toast.showInfo('MFA.TOAST.DELETED', true); - this.refreshPageAfterTimout(2000); - }); - } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - const req = new MgmtRemoveSecondFactorFromLoginPolicyRequest(); - req.setType(type as SecondFactorType); - (this.service as ManagementService).removeSecondFactorFromLoginPolicy(req).then(() => { - this.toast.showInfo('MFA.TOAST.DELETED', true); - this.refreshPageAfterTimout(2000); - }); - } - } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { - if (this.componentType === LoginMethodComponentType.MultiFactor) { - const req = new AdminRemoveMultiFactorFromLoginPolicyRequest(); - req.setType(type as MultiFactorType); - (this.service as AdminService).removeMultiFactorFromLoginPolicy(req).then(() => { - this.toast.showInfo('MFA.TOAST.DELETED', true); - this.refreshPageAfterTimout(2000); - }); - } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - const req = new AdminRemoveSecondFactorFromLoginPolicyRequest(); - req.setType(type as SecondFactorType); - (this.service as AdminService).removeSecondFactorFromLoginPolicy(req).then(() => { - this.toast.showInfo('MFA.TOAST.DELETED', true); - this.refreshPageAfterTimout(2000); - }); - } - } - } - }); - } - - public addMfa(): void { - - let selection: any[] = []; - - if (this.componentType === LoginMethodComponentType.MultiFactor) { - selection = [MultiFactorType.MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION]; - } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - selection = [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP]; - } - - this.mfas.forEach(mfa => { - const index = selection.findIndex(sel => sel === mfa); - if (index > -1) { - selection.splice(index, 1); - } - }); - - const dialogRef = this.dialog.open(DialogAddTypeComponent, { - data: { - title: 'MFA.CREATE.TITLE', - desc: 'MFA.CREATE.DESCRIPTION', - componentType: this.componentType, - types: selection, - }, - width: '400px', - }); - - dialogRef.afterClosed().subscribe((mfaType: MultiFactorType | SecondFactorType) => { - if (mfaType) { - if (this.serviceType === PolicyComponentServiceType.MGMT) { - if (this.componentType === LoginMethodComponentType.MultiFactor) { - const req = new MgmtAddMultiFactorToLoginPolicyRequest(); - req.setType(mfaType as MultiFactorType); - (this.service as ManagementService).addMultiFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); - } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - const req = new MgmtAddSecondFactorToLoginPolicyRequest(); - req.setType(mfaType as SecondFactorType); - (this.service as ManagementService).addSecondFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); - } - } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { - if (this.componentType === LoginMethodComponentType.MultiFactor) { - const req = new AdminAddMultiFactorToLoginPolicyRequest(); - req.setType(mfaType as MultiFactorType); - (this.service as AdminService).addMultiFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); - } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - const req = new AdminAddSecondFactorToLoginPolicyRequest(); - req.setType(mfaType as SecondFactorType); - (this.service as AdminService).addSecondFactorToLoginPolicy(req).then(() => { - this.refreshPageAfterTimout(2000); - }).catch(error => { - this.toast.showError(error); - }); - } - } - } - }); - } - - private async getData(): Promise { - this.loadingSubject.next(true); - - if (this.serviceType === PolicyComponentServiceType.MGMT) { - if (this.componentType === LoginMethodComponentType.MultiFactor) { - (this.service as ManagementService).listLoginPolicyMultiFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); - } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - (this.service as ManagementService).listLoginPolicySecondFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); - } - } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { - if (this.componentType === LoginMethodComponentType.MultiFactor) { - (this.service as AdminService).listLoginPolicyMultiFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); - } else if (this.componentType === LoginMethodComponentType.SecondFactor) { - (this.service as AdminService).listLoginPolicySecondFactors().then(resp => { - this.mfas = resp.resultList; - this.loadingSubject.next(false); - }).catch(error => { - this.toast.showError(error); - this.loadingSubject.next(false); - }); - } - } - } - - public refreshPageAfterTimout(to: number): void { - setTimeout(() => { - this.getData(); - }, to); - } -} diff --git a/console/src/app/modules/mfa-table/mfa-table.module.ts b/console/src/app/modules/mfa-table/mfa-table.module.ts deleted file mode 100644 index 7c4b890b4a..0000000000 --- a/console/src/app/modules/mfa-table/mfa-table.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatIconModule } from '@angular/material/icon'; -import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatTableModule } from '@angular/material/table'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { RouterModule } from '@angular/router'; -import { TranslateModule } from '@ngx-translate/core'; -import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; -import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module'; -import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module'; -import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module'; -import { TruncatePipeModule } from 'src/app/pipes/truncate-pipe/truncate-pipe.module'; - -import { MfaTableComponent } from './mfa-table.component'; -import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component'; -import { InputModule } from '../input/input.module'; -import { MatSelectModule } from '@angular/material/select'; -import { MatRippleModule } from '@angular/material/core'; - -@NgModule({ - declarations: [MfaTableComponent, DialogAddTypeComponent], - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatIconModule, - InputModule, - MatSelectModule, - MatTooltipModule, - TranslateModule, - TimestampToDatePipeModule, - HasRoleModule, - MatProgressSpinnerModule, - MatRippleModule, - ], - exports: [ - MfaTableComponent, - ], -}) -export class MfaTableModule { } diff --git a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.module.ts b/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.module.ts deleted file mode 100644 index 8c125514c5..0000000000 --- a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatSelectModule } from '@angular/material/select'; -import { TranslateModule } from '@ngx-translate/core'; -import { InputModule } from 'src/app/modules/input/input.module'; - -import { AddIdpDialogComponent } from './add-idp-dialog.component'; - -@NgModule({ - declarations: [AddIdpDialogComponent], - imports: [ - CommonModule, - MatDialogModule, - MatButtonModule, - TranslateModule, - InputModule, - MatSelectModule, - FormsModule, - ], -}) -export class AddIdpDialogModule { } diff --git a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.html b/console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.html similarity index 95% rename from console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.html rename to console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.html index 03cfaf280b..4d7b9f6429 100644 --- a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.html @@ -13,8 +13,6 @@ -

{{'LOGINPOLICY.ADDIDP.SELECTIDPS' | translate}}

- {{ 'LOGINPOLICY.ADDIDP.SELECTIDPS' | translate }} diff --git a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.scss b/console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.scss similarity index 100% rename from console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.scss rename to console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.scss diff --git a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.spec.ts b/console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.spec.ts similarity index 100% rename from console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.spec.ts rename to console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.spec.ts diff --git a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.ts b/console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.ts similarity index 97% rename from console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.ts rename to console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.ts index 7b7843e8c8..65a82691e2 100644 --- a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy-idps/add-idp-dialog/add-idp-dialog.component.ts @@ -5,7 +5,7 @@ import { IDPQuery } from 'src/app/proto/generated/zitadel/management_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; -import { PolicyComponentServiceType } from '../../policy-component-types.enum'; +import { PolicyComponentServiceType } from '../../../policy-component-types.enum'; @Component({ selector: 'app-add-idp-dialog', diff --git a/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.html b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.html new file mode 100644 index 0000000000..c98839cd3a --- /dev/null +++ b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.html @@ -0,0 +1,21 @@ +
+
+ google +
+ {{idp.idpName}} + {{ 'IDP.TYPES.'+idp.idpType | translate }} + +
+ + +
+ +
\ No newline at end of file diff --git a/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.scss b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.scss new file mode 100644 index 0000000000..6bd2af4c1b --- /dev/null +++ b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.scss @@ -0,0 +1,63 @@ +.idps { + display: flex; + flex-direction: column; + + .idp { + display: flex; + align-items: center; + padding: 10px; + border-radius: .5rem; + position: relative; + margin-bottom: .5rem; + min-height: 40px; + + img { + height: 30px; + width: 30px; + margin: .5rem; + border-radius: .5rem; + object-fit: contain; + } + + div { + display: block; + margin-left: .5rem; + + * { + display: block; + } + } + + .meta-info { + font-size: 14px; + color: var(--grey); + } + + .fill-space { + flex: 1; + } + + .rm { + display: none; + transition: all .2s ease; + cursor: pointer; + } + + &:hover { + .rm { + display: block; + } + } + } + + .new-idp { + display: flex; + align-items: center; + align-self: flex-end; + margin-top: .5rem; + + .icon { + margin-left: .5rem; + } + } +} diff --git a/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.spec.ts b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.spec.ts new file mode 100644 index 0000000000..694a1cd15e --- /dev/null +++ b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginPolicyIdpsComponent } from './login-policy-idps.component'; + +describe('LoginPolicyIdpsComponent', () => { + let component: LoginPolicyIdpsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [LoginPolicyIdpsComponent], + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginPolicyIdpsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.ts b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.ts new file mode 100644 index 0000000000..312b87309b --- /dev/null +++ b/console/src/app/modules/policies/login-policy/login-policy-idps/login-policy-idps.component.ts @@ -0,0 +1,108 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { IDP, IDPLoginPolicyLink, IDPOwnerType, IDPStylingType } from 'src/app/proto/generated/zitadel/idp_pb'; +import { AdminService } from 'src/app/services/admin.service'; +import { ManagementService } from 'src/app/services/mgmt.service'; +import { ToastService } from 'src/app/services/toast.service'; + +import { PolicyComponentServiceType } from '../../policy-component-types.enum'; +import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component'; + +@Component({ + selector: 'cnsl-login-policy-idps', + templateUrl: './login-policy-idps.component.html', + styleUrls: ['./login-policy-idps.component.scss'], +}) +export class LoginPolicyIdpsComponent implements OnInit { + @Input() public disabled: boolean = true; + @Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; + @Input() public service!: ManagementService | AdminService; + public loading: boolean = false; + + public idps: IDPLoginPolicyLink.AsObject[] = []; + + public IDPStylingType: any = IDPStylingType; + + constructor( + private toast: ToastService, + private dialog: MatDialog, + ) { } + + ngOnInit(): void { + this.getIdps().then(resp => { + this.idps = resp; + console.log(this.idps); + }); + } + + private async getIdps(): Promise { + switch (this.serviceType) { + case PolicyComponentServiceType.MGMT: + return (this.service as ManagementService).listLoginPolicyIDPs() + .then((resp) => { + return resp.resultList; + }); + case PolicyComponentServiceType.ADMIN: + return (this.service as AdminService).listLoginPolicyIDPs() + .then((providers) => { + return providers.resultList; + }); + } + } + + private addIdp(idp: IDP.AsObject | IDP.AsObject, ownerType: IDPOwnerType): Promise { + switch (this.serviceType) { + case PolicyComponentServiceType.MGMT: + return (this.service as ManagementService).addIDPToLoginPolicy(idp.id, ownerType); + case PolicyComponentServiceType.ADMIN: + return (this.service as AdminService).addIDPToLoginPolicy(idp.id); + } + } + + public openDialog(): void { + const dialogRef = this.dialog.open(AddIdpDialogComponent, { + data: { + serviceType: this.serviceType, + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp && resp.idp && resp.type) { + this.addIdp(resp.idp, resp.type).then(() => { + this.loading = true; + setTimeout(() => { + this.getIdps(); + }, 1000); + }).catch(error => { + this.toast.showError(error); + }); + } + }); + } + + public removeIdp(idp: IDPLoginPolicyLink.AsObject): void { + switch (this.serviceType) { + case PolicyComponentServiceType.MGMT: + (this.service as ManagementService).removeIDPFromLoginPolicy(idp.idpId).then(() => { + const index = this.idps.findIndex(temp => temp === idp); + if (index > -1) { + this.idps.splice(index, 1); + } + }, error => { + this.toast.showError(error); + }); + break; + case PolicyComponentServiceType.ADMIN: + (this.service as AdminService).removeIDPFromLoginPolicy(idp.idpId).then(() => { + const index = this.idps.findIndex(temp => temp === idp); + if (index > -1) { + this.idps.splice(index, 1); + } + }, error => { + this.toast.showError(error); + }); + break; + } + } +} diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html index b83f9e6e6b..4ec6f8b847 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy.component.html @@ -6,131 +6,35 @@
+ - - - + + + - - - - + + + +
-
-
- - {{'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}} - - - - + - - - {{'POLICY.DATA.ALLOWUSERNAMEPASSWORD_DESC' | translate}} - - -
-
- - {{'POLICY.DATA.ALLOWREGISTER' | translate}} - - - - - - - - - {{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}} - - -
-
- - {{'POLICY.DATA.ALLOWEXTERNALIDP' | translate}} - - - - - - - - - {{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}} - - -
-
- - {{'POLICY.DATA.FORCEMFA' | translate}} - - - - - - - - - {{'POLICY.DATA.FORCEMFA_DESC' | translate}} - - -
-
- - {{'POLICY.DATA.HIDEPASSWORDRESET' | translate}} - - - - + + + + + - - - {{'POLICY.DATA.HIDEPASSWORDRESET_DESC' | translate}} - - -
- -
- - {{'LOGINPOLICY.PASSWORDLESS' | translate}} - - - {{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}} - - - - - - - -
-
- - - -
- - -

{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}

-

{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}

- + @@ -139,62 +43,134 @@ [componentType]="LoginMethodComponentType.MultiFactor" [disabled]="(([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false) || (serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false)"> + -

{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}

-

{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}

+ + + + - - - + + + - - -
+ +
+
+ + {{'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}} + -

{{'LOGINPOLICY.IDPS' | translate}}

+ + + - - - + + + {{'POLICY.DATA.ALLOWUSERNAMEPASSWORD_DESC' | translate}} + + +
+
+ + {{'POLICY.DATA.ALLOWREGISTER' | translate}} + -
-
- -
- google -
- {{idp.idpName}} - {{ 'IDP.TYPE' | translate }}: {{ 'IDP.TYPES.'+idp.idpType | - translate - }} - {{ 'IDP.ID' | translate }}: {{idp.idpId}} -
-
+ + + + + + + {{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}} + + +
+
+ + {{'POLICY.DATA.ALLOWEXTERNALIDP' | translate}} + + + + + + + + + {{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}} + + +
+
+ + {{'POLICY.DATA.FORCEMFA' | translate}} + + + + + + + + + {{'POLICY.DATA.FORCEMFA_DESC' | translate}} + + +
+
+ + {{'POLICY.DATA.HIDEPASSWORDRESET' | translate}} + + + + + + + + + {{'POLICY.DATA.HIDEPASSWORDRESET_DESC' | translate}} + +
-
- add -
-
+ +
+ + {{'LOGINPOLICY.PASSWORDLESS' | translate}} + + + {{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}} + + + + + + + +
+
+ + + + +
- + - - - + + + diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.scss b/console/src/app/modules/policies/login-policy/login-policy.component.scss index 3105e4bf1a..c917b2bbc0 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.scss +++ b/console/src/app/modules/policies/login-policy/login-policy.component.scss @@ -37,95 +37,6 @@ font-size: 14px; } -.idps { - display: flex; - margin: 0 -.5rem; - - .idp { - background-color: #506e6e; - color: white; - - .line { - display: flex; - align-items: center; - - img { - height: 30px; - width: 30px; - margin-right: 1rem; - border-radius: .5rem; - object-fit: contain; - } - - div { - flex: 1; - display: block; - - * { - display: block; - } - } - } - } - - .idp, - .new-idp { - display: grid; - align-items: center; - justify-content: center; - margin: .5rem; - padding: 10px; - border: 1px solid var(--grey); - border-radius: .5rem; - position: relative; - min-height: 70px; - min-width: 150px; - - .name { - font-weight: 700; - } - - span { - padding: 2px; - } - - .rm { - color: var(--warn); - position: absolute; - display: none; - top: -2px; - transition: all .2s ease; - left: -2px; - transform: translateX(-50%) translateY(-50%); - cursor: pointer; - - &[disabled] { - display: none; - } - } - - &:not(.disabled) { - cursor: default; - - &:hover { - .rm { - display: block; - } - } - } - - img { - height: 100%; - width: 100%; - object-fit: scale-down; - } - } - - .new-idp:not(.disabled) { - cursor: pointer; - } -} - .divider { width: 100%; height: 1px; diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.ts b/console/src/app/modules/policies/login-policy/login-policy.component.ts index b054af484c..69d2c8e91f 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.component.ts @@ -1,15 +1,12 @@ import { Component, Injector, OnDestroy, Type } from '@angular/core'; -import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { LoginMethodComponentType } from 'src/app/modules/mfa-table/mfa-table.component'; import { GetLoginPolicyResponse as AdminGetLoginPolicyResponse, UpdateLoginPolicyRequest, UpdateLoginPolicyResponse, } from 'src/app/proto/generated/zitadel/admin_pb'; -import { IDP, IDPLoginPolicyLink, IDPOwnerType, IDPStylingType } from 'src/app/proto/generated/zitadel/idp_pb'; import { AddCustomLoginPolicyRequest, GetLoginPolicyResponse as MgmtGetLoginPolicyResponse, @@ -21,7 +18,7 @@ import { ToastService } from 'src/app/services/toast.service'; import { GridPolicy, LOGIN_POLICY } from '../../policy-grid/policies'; import { PolicyComponentServiceType } from '../policy-component-types.enum'; -import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component'; +import { LoginMethodComponentType } from './mfa-table/mfa-table.component'; @Component({ selector: 'app-login-policy', @@ -37,17 +34,14 @@ export class LoginPolicyComponent implements OnDestroy { public service!: ManagementService | AdminService; public PolicyComponentServiceType: any = PolicyComponentServiceType; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; - public idps: IDPLoginPolicyLink.AsObject[] = []; public loading: boolean = false; public disabled: boolean = true; - public IDPStylingType: any = IDPStylingType; public currentPolicy: GridPolicy = LOGIN_POLICY; constructor( private route: ActivatedRoute, private toast: ToastService, - private dialog: MatDialog, private injector: Injector, ) { this.sub = this.route.data.pipe(switchMap(data => { @@ -83,9 +77,7 @@ export class LoginPolicyComponent implements OnDestroy { this.disabled = this.isDefault; } }); - this.getIdps().then(resp => { - this.idps = resp; - }); + } public ngOnDestroy(): void { @@ -102,21 +94,6 @@ export class LoginPolicyComponent implements OnDestroy { } } - private async getIdps(): Promise { - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - return (this.service as ManagementService).listLoginPolicyIDPs() - .then((resp) => { - return resp.resultList; - }); - case PolicyComponentServiceType.ADMIN: - return (this.service as AdminService).listLoginPolicyIDPs() - .then((providers) => { - return providers.resultList; - }); - } - } - private async updateData(): Promise { switch (this.serviceType) { @@ -172,62 +149,6 @@ export class LoginPolicyComponent implements OnDestroy { } } - public openDialog(): void { - const dialogRef = this.dialog.open(AddIdpDialogComponent, { - data: { - serviceType: this.serviceType, - }, - width: '400px', - }); - - dialogRef.afterClosed().subscribe(resp => { - if (resp && resp.idp && resp.type) { - this.addIdp(resp.idp, resp.type).then(() => { - this.loading = true; - setTimeout(() => { - this.fetchData(); - }, 2000); - }).catch(error => { - this.toast.showError(error); - }); - } - }); - } - - private addIdp(idp: IDP.AsObject | IDP.AsObject, ownerType: IDPOwnerType): Promise { - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - return (this.service as ManagementService).addIDPToLoginPolicy(idp.id, ownerType); - case PolicyComponentServiceType.ADMIN: - return (this.service as AdminService).addIDPToLoginPolicy(idp.id); - } - } - - public removeIdp(idp: IDPLoginPolicyLink.AsObject): void { - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - (this.service as ManagementService).removeIDPFromLoginPolicy(idp.idpId).then(() => { - const index = this.idps.findIndex(temp => temp === idp); - if (index > -1) { - this.idps.splice(index, 1); - } - }, error => { - this.toast.showError(error); - }); - break; - case PolicyComponentServiceType.ADMIN: - (this.service as AdminService).removeIDPFromLoginPolicy(idp.idpId).then(() => { - const index = this.idps.findIndex(temp => temp === idp); - if (index > -1) { - this.idps.splice(index, 1); - } - }, error => { - this.toast.showError(error); - }); - break; - } - } - public get isDefault(): boolean { if (this.loginData && this.serviceType === PolicyComponentServiceType.MGMT) { return (this.loginData as LoginPolicy.AsObject).isDefault; diff --git a/console/src/app/modules/policies/login-policy/login-policy.module.ts b/console/src/app/modules/policies/login-policy/login-policy.module.ts index 206820d454..f0d9a3a489 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.module.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.module.ts @@ -3,6 +3,7 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatRippleModule } from '@angular/material/core'; +import { MatDialogModule } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; @@ -14,18 +15,26 @@ import { CardModule } from 'src/app/modules/card/card.module'; import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module'; import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module'; import { InputModule } from 'src/app/modules/input/input.module'; -import { MfaTableModule } from 'src/app/modules/mfa-table/mfa-table.module'; import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { InfoSectionModule } from '../../info-section/info-section.module'; import { PolicyGridModule } from '../../policy-grid/policy-grid.module'; -import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module'; +import { AddIdpDialogComponent } from './login-policy-idps/add-idp-dialog/add-idp-dialog.component'; +import { LoginPolicyIdpsComponent } from './login-policy-idps/login-policy-idps.component'; import { LoginPolicyRoutingModule } from './login-policy-routing.module'; import { LoginPolicyComponent } from './login-policy.component'; +import { DialogAddTypeComponent } from './mfa-table/dialog-add-type/dialog-add-type.component'; +import { MfaTableComponent } from './mfa-table/mfa-table.component'; @NgModule({ - declarations: [LoginPolicyComponent], + declarations: [ + LoginPolicyComponent, + LoginPolicyIdpsComponent, + MfaTableComponent, + DialogAddTypeComponent, + AddIdpDialogComponent, + ], imports: [ LoginPolicyRoutingModule, CommonModule, @@ -33,21 +42,20 @@ import { LoginPolicyComponent } from './login-policy.component'; FormsModule, CardModule, InputModule, + MatIconModule, MatButtonModule, HasFeaturePipeModule, MatSlideToggleModule, - MatIconModule, HasRoleModule, + MatDialogModule, HasRolePipeModule, MatTooltipModule, - TranslateModule, DetailLayoutModule, - AddIdpDialogModule, IdpTableModule, - MfaTableModule, MatProgressSpinnerModule, MatSelectModule, MatRippleModule, + TranslateModule, PolicyGridModule, ], }) diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html b/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.html similarity index 100% rename from console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html rename to console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.html diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss b/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.scss similarity index 100% rename from console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss rename to console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.scss diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts b/console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.ts similarity index 100% rename from console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts rename to console/src/app/modules/policies/login-policy/mfa-table/dialog-add-type/dialog-add-type.component.ts diff --git a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.html b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.html new file mode 100644 index 0000000000..28b5e3cbf1 --- /dev/null +++ b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.html @@ -0,0 +1,19 @@ +
+ +
+
+
+ + {{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': + LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}} + + + +
+ +
\ No newline at end of file diff --git a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.scss b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.scss new file mode 100644 index 0000000000..f43b5f0729 --- /dev/null +++ b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.scss @@ -0,0 +1,41 @@ +.mfa-list { + display: flex; + flex-direction: column; + + .mfa { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px; + border-radius: .5rem; + margin-bottom: .5rem; + min-height: 40px; + + .fill-space { + flex: 1; + } + + .rm { + display: none; + transition: all .2s ease; + cursor: pointer; + } + + &:hover { + .rm { + display: block; + } + } + } + + .new-mfa { + display: flex; + align-items: center; + align-self: flex-end; + margin-top: .5rem; + + .icon { + margin-left: .5rem; + } + } +} diff --git a/console/src/app/modules/mfa-table/mfa-table.component.spec.ts b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.spec.ts similarity index 100% rename from console/src/app/modules/mfa-table/mfa-table.component.spec.ts rename to console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.spec.ts diff --git a/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.ts b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.ts new file mode 100644 index 0000000000..e54b372a75 --- /dev/null +++ b/console/src/app/modules/policies/login-policy/mfa-table/mfa-table.component.ts @@ -0,0 +1,224 @@ +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatPaginator } from '@angular/material/paginator'; +import { TranslateService } from '@ngx-translate/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; +import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; +import { + AddMultiFactorToLoginPolicyRequest as AdminAddMultiFactorToLoginPolicyRequest, + AddSecondFactorToLoginPolicyRequest as AdminAddSecondFactorToLoginPolicyRequest, + RemoveMultiFactorFromLoginPolicyRequest as AdminRemoveMultiFactorFromLoginPolicyRequest, + RemoveSecondFactorFromLoginPolicyRequest as AdminRemoveSecondFactorFromLoginPolicyRequest, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { + AddMultiFactorToLoginPolicyRequest as MgmtAddMultiFactorToLoginPolicyRequest, + AddSecondFactorToLoginPolicyRequest as MgmtAddSecondFactorToLoginPolicyRequest, + RemoveMultiFactorFromLoginPolicyRequest as MgmtRemoveMultiFactorFromLoginPolicyRequest, + RemoveSecondFactorFromLoginPolicyRequest as MgmtRemoveSecondFactorFromLoginPolicyRequest, +} from 'src/app/proto/generated/zitadel/management_pb'; +import { MultiFactorType, SecondFactorType } from 'src/app/proto/generated/zitadel/policy_pb'; +import { AdminService } from 'src/app/services/admin.service'; +import { ManagementService } from 'src/app/services/mgmt.service'; +import { ToastService } from 'src/app/services/toast.service'; + +import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component'; + +export enum LoginMethodComponentType { + MultiFactor = 1, + SecondFactor = 2, +} + +@Component({ + selector: 'app-mfa-table', + templateUrl: './mfa-table.component.html', + styleUrls: ['./mfa-table.component.scss'], +}) +export class MfaTableComponent implements OnInit { + public LoginMethodComponentType: any = LoginMethodComponentType; + @Input() componentType!: LoginMethodComponentType; + @Input() public serviceType!: PolicyComponentServiceType; + @Input() service!: AdminService | ManagementService; + @Input() disabled: boolean = false; + @ViewChild(MatPaginator) public paginator!: MatPaginator; + public mfas: Array = []; + + private loadingSubject: BehaviorSubject = new BehaviorSubject(false); + public loading$: Observable = this.loadingSubject.asObservable(); + + public PolicyComponentServiceType: any = PolicyComponentServiceType; + + constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { } + + public ngOnInit(): void { + this.getData(); + } + + public removeMfa(type: MultiFactorType | SecondFactorType): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'MFA.DELETE.TITLE', + descriptionKey: 'MFA.DELETE.DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new MgmtRemoveMultiFactorFromLoginPolicyRequest(); + req.setType(type as MultiFactorType); + (this.service as ManagementService).removeMultiFactorFromLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new MgmtRemoveSecondFactorFromLoginPolicyRequest(); + req.setType(type as SecondFactorType); + (this.service as ManagementService).removeSecondFactorFromLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new AdminRemoveMultiFactorFromLoginPolicyRequest(); + req.setType(type as MultiFactorType); + (this.service as AdminService).removeMultiFactorFromLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new AdminRemoveSecondFactorFromLoginPolicyRequest(); + req.setType(type as SecondFactorType); + (this.service as AdminService).removeSecondFactorFromLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } + } + } + }); + } + + public addMfa(): void { + + let selection: any[] = []; + + if (this.componentType === LoginMethodComponentType.MultiFactor) { + selection = [MultiFactorType.MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION]; + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + selection = [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP]; + } + + this.mfas.forEach(mfa => { + const index = selection.findIndex(sel => sel === mfa); + if (index > -1) { + selection.splice(index, 1); + } + }); + + const dialogRef = this.dialog.open(DialogAddTypeComponent, { + data: { + title: 'MFA.CREATE.TITLE', + desc: 'MFA.CREATE.DESCRIPTION', + componentType: this.componentType, + types: selection, + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe((mfaType: MultiFactorType | SecondFactorType) => { + if (mfaType) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new MgmtAddMultiFactorToLoginPolicyRequest(); + req.setType(mfaType as MultiFactorType); + (this.service as ManagementService).addMultiFactorToLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new MgmtAddSecondFactorToLoginPolicyRequest(); + req.setType(mfaType as SecondFactorType); + (this.service as ManagementService).addSecondFactorToLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new AdminAddMultiFactorToLoginPolicyRequest(); + req.setType(mfaType as MultiFactorType); + (this.service as AdminService).addMultiFactorToLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new AdminAddSecondFactorToLoginPolicyRequest(); + req.setType(mfaType as SecondFactorType); + (this.service as AdminService).addSecondFactorToLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } + } + } + }); + } + + private async getData(): Promise { + this.loadingSubject.next(true); + + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + (this.service as ManagementService).listLoginPolicyMultiFactors().then(resp => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + (this.service as ManagementService).listLoginPolicySecondFactors().then(resp => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } + } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + (this.service as AdminService).listLoginPolicyMultiFactors().then(resp => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + (this.service as AdminService).listLoginPolicySecondFactors().then(resp => { + this.mfas = resp.resultList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } + } + } + + public refreshPageAfterTimout(to: number): void { + setTimeout(() => { + this.getData(); + }, to); + } +} diff --git a/console/src/app/modules/user-grants/user-grants.component.html b/console/src/app/modules/user-grants/user-grants.component.html index 71a5cade46..1f4617f250 100644 --- a/console/src/app/modules/user-grants/user-grants.component.html +++ b/console/src/app/modules/user-grants/user-grants.component.html @@ -1,180 +1,181 @@ - - - + [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp" + [dataSize]="dataSource?.totalResult" [selection]="selection"> + + + - - - add{{ 'GRANTS.ADD_BTN' | translate }} - + + + add{{ 'GRANTS.ADD_BTN' | translate }} + -
- - - - +
+
- - - - - - - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + {{role}} + + + + + + + - - - - + + + +
+ + + + + + + + {{ 'PROJECT.GRANT.USER' | translate }} + + + {{grant?.displayName}} + {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }} + + + {{grant.orgName}} {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} + + + {{grant.projectName}} DATES +
+ {{ 'PROJECT.GRANT.CREATIONDATE' | translate }}: + {{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} +
+
+ {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} + {{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} +
+
{{'PROJECT.GRANT.CREATIONDATE' | translate}} + {{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} + {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} + {{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} + + {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} + + + +
+
+ {{ role }} +
+ + +
+
+ +
+ + + + + {{role.key}} + + + + - -
{{ 'PROJECT.GRANT.USER' | translate }} - - - {{grant?.displayName}} {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }} - - - {{grant.orgName}}
- - {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} - - - - {{grant.projectName}} - - - - DATES - -
- {{ 'PROJECT.GRANT.CREATIONDATE' | translate }}: - {{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} -
-
- {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} - {{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} -
- -
- - - {{'PROJECT.GRANT.CREATIONDATE' | translate}} - - {{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} - - - - - {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} - - {{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} - - - - - - {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} - - - - -
-
- {{ role }} -
- - -
-
- -
- - - - - {{role.key}} - - - - - - - - - - - {{role}} - - - - - -
- -
- - - - - - -
- - {{'GRANTS.EMPTY' | translate}} -
- - +
+ + {{'GRANTS.EMPTY' | translate}}
+ + +
- + \ No newline at end of file diff --git a/console/src/app/modules/user-grants/user-grants.component.scss b/console/src/app/modules/user-grants/user-grants.component.scss index 236e22c9c2..6de0262fef 100644 --- a/console/src/app/modules/user-grants/user-grants.component.scss +++ b/console/src/app/modules/user-grants/user-grants.component.scss @@ -21,6 +21,11 @@ } } + .user { + text-decoration: none; + color: inherit; + } + th { .search-button { visibility: hidden; diff --git a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.ts b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.ts index ec783e9316..e81b1b9d73 100644 --- a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.ts +++ b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-grid/granted-project-grid.component.ts @@ -82,7 +82,7 @@ export class GrantedProjectGridComponent implements OnChanges { private async getPrefixedItem(key: string): Promise { const org = this.storage.getItem(StorageKey.organization, StorageLocation.session) as Org.AsObject; - return localStorage.getItem(`${org.id}:${key}`); + return localStorage.getItem(`${org?.id}:${key}`); } private async setPrefixedItem(key: string, value: any): Promise { diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html index cc66d4c037..699b9d7e22 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html @@ -1,148 +1,153 @@ -
-
- - arrow_back - -

{{ 'PROJECT.PAGES.TITLE' | translate }} {{project?.name}}

+
+
+ + arrow_back + +

{{ 'PROJECT.PAGES.TITLE' | translate }} {{project?.name}}

- + - - - - + + + + - + - + - - - - - + + + + + -
-

{{ 'PROJECT.PAGES.DESCRIPTION' | translate }}

-

{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}

-
-
- - -
-

{{'PROJECT.PAGES.PRIVATELABEL.TITLE' | translate}}

-

- {{'PROJECT.PAGES.PRIVATELABEL.'+project.privateLabelingSetting+'.TITLE' | translate}} - -

-
- - - - -
- -
- -
-
- - - - - - - - - - - -

{{'PROJECT.ROLE.OPTIONS' | translate}}

- - {{'PROJECT.ROLE.ASSERTION' | translate}} -

{{'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate}}

- - {{'PROJECT.ROLE.CHECK' | translate}} -

{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}

- - {{'PROJECT.HAS_PROJECT' | translate}} -

{{'PROJECT.HAS_PROJECT_DESCRIPTION' | translate}}

-
- - -
-
- - - - - - - -
-
+
+

{{ 'PROJECT.PAGES.DESCRIPTION' | translate }}

+

{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}

+
-
-
-
- {{'RESOURCEID' | translate}}: - {{ projectId }} -
-
- {{'PROJECT.STATE.TITLE' | translate}}: - {{'PROJECT.STATE.'+project.state - | translate}} -
-
- - - - - - - - - + +
+

{{'PROJECT.PAGES.PRIVATELABEL.TITLE' | translate}}

+

+ {{'PROJECT.PAGES.PRIVATELABEL.'+project.privateLabelingSetting+'.TITLE' | translate}} + +

+
+ + + + + +
+ +
+ +
+
+ + + + + + + + + + + +

{{'PROJECT.ROLE.OPTIONS' | translate}}

+ + {{'PROJECT.ROLE.ASSERTION' | translate}} +

{{'PROJECT.ROLE.ASSERTION_DESCRIPTION' | translate}}

+ + {{'PROJECT.ROLE.CHECK' | translate}} +

{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}

+ + {{'PROJECT.HAS_PROJECT' | translate}} +

{{'PROJECT.HAS_PROJECT_DESCRIPTION' | translate}}

+
+ + +
+
+ + + + + + + +
+
+
+
+
+
+ {{'RESOURCEID' | translate}}: + {{ projectId }} +
+
+ {{'PROJECT.STATE.TITLE' | translate}}: + {{'PROJECT.STATE.'+project.state + | translate}} +
- + + + + + + + + + + +
+ \ No newline at end of file diff --git a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts index 5c291bd225..41bfa06ba0 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-grid/owned-project-grid.component.ts @@ -106,7 +106,7 @@ export class OwnedProjectGridComponent implements OnChanges { private async getPrefixedItem(key: string): Promise { const org = this.storage.getItem(StorageKey.organization, StorageLocation.session) as Org.AsObject; - return localStorage.getItem(`${org.id}:${key}`); + return localStorage.getItem(`${org?.id}:${key}`); } private async setPrefixedItem(key: string, value: any): Promise { diff --git a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html index eb101d1fbb..3a5ffd08ef 100644 --- a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html +++ b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html @@ -1,57 +1,64 @@ -
-
-
- {{'PROJECT.GRANT.DETAIL.PROJECTNAME' | translate}} - - {{grant?.projectName}} -
-
- {{'PROJECT.GRANT.DETAIL.RESOURCEOWNER' | translate}} - - {{grant?.details?.resourceOwner}} -
-
- + description="{{ 'PROJECT.GRANT.DETAIL.DESC' | translate }}"> +
+
+
+ {{'PROJECT.GRANT.DETAIL.PROJECTNAME' | translate}} - -
- - -
+ {{grant?.projectName}} +
+
+ {{'PROJECT.GRANT.DETAIL.RESOURCEOWNER' | translate}} + + {{grant?.projectOwnerName}} +
+
+ {{'PROJECT.GRANT.DETAIL.GRANTEDORG' | translate}} + + {{grant?.grantedOrgName}} +
- - {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} - - - {{role.key}} - - - + -
+
+ + +
+
-

{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}

-

{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}

+ + {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} + + + {{role.key}} + + + - - - - add{{ 'ACTIONS.NEW' | translate }} - - +
+ +

{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}

+

{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}

+ + + + + add{{ 'ACTIONS.NEW' | translate }} + + \ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html index e6468e50ed..8f8ea8175e 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html @@ -1,135 +1,136 @@ +
{{error}}
+ -
-
- - arrow_back - -
-

{{user.human ? user.human?.profile?.displayName : user.machine?.name}}

-

{{user?.preferredLoginName}}

-
- - - - - - - - - - - - - -
- - - - {{'USER.PAGES.LOCKEDDESCRIPTION' | translate}} - {{ 'USER.PAGES.NOUSER' | translate }} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- {{'RESOURCEID' | translate}}: - {{ user.id }} -
-
- {{'USER.PREFERRED_LOGINNAME' | translate}} - {{user.preferredLoginName}} -
-
- {{'USER.PAGES.STATE' | translate}} - {{'USER.DATA.STATE'+user.state - | translate}} -
+
+
+ + arrow_back + +
+

{{user.human ? user.human?.profile?.displayName : user.machine?.name}}

+

{{user?.preferredLoginName}}

- - -
- - - -
-
- - - - -
+ + + + + + + + + + + +
+ + + + + {{'USER.PAGES.LOCKEDDESCRIPTION' | translate}} + {{ 'USER.PAGES.NOUSER' | translate }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ {{'RESOURCEID' | translate}}: + {{ user.id }} +
+
+ {{'USER.PREFERRED_LOGINNAME' | translate}} + {{user.preferredLoginName}} +
+
+ {{'USER.PAGES.STATE' | translate}} + {{'USER.DATA.STATE'+user.state + | translate}} +
+
+ + + +
+ + + +
+
+ + + + +
+
\ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts index 48198197d3..389ad960bd 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts @@ -37,6 +37,8 @@ export class UserDetailComponent implements OnInit { public EditDialogType: any = EditDialogType; public refreshChanges$: EventEmitter = new EventEmitter(); + public error: string = ''; + constructor( public translate: TranslateService, private route: ActivatedRoute, @@ -56,7 +58,8 @@ export class UserDetailComponent implements OnInit { this.user = resp.user; } }).catch(err => { - console.error(err); + this.error = err.message ?? ''; + this.toast.showError(err); }); this.mgmtUserService.listUserMetadata(id, 0, 100, []).then(resp => { diff --git a/console/src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module.ts b/console/src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module.ts index 505334a36a..d3171d615b 100644 --- a/console/src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module.ts +++ b/console/src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module.ts @@ -5,14 +5,14 @@ import { TimestampToDatePipe } from './timestamp-to-date.pipe'; @NgModule({ - declarations: [ - TimestampToDatePipe, - ], - imports: [ - CommonModule, - ], - exports: [ - TimestampToDatePipe, - ], + declarations: [ + TimestampToDatePipe, + ], + imports: [ + CommonModule, + ], + exports: [ + TimestampToDatePipe, + ], }) export class TimestampToDatePipeModule { } diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index 8e2c2cc82d..59964806fc 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -9,6 +9,8 @@ import { AddIAMMemberResponse, AddIDPToLoginPolicyRequest, AddIDPToLoginPolicyResponse, + AddJWTIDPRequest, + AddJWTIDPResponse, AddMultiFactorToLoginPolicyRequest, AddMultiFactorToLoginPolicyResponse, AddOIDCIDPRequest, @@ -144,6 +146,8 @@ import { UpdateCustomOrgIAMPolicyResponse, UpdateIAMMemberRequest, UpdateIAMMemberResponse, + UpdateIDPJWTConfigRequest, + UpdateIDPJWTConfigResponse, UpdateIDPOIDCConfigRequest, UpdateIDPOIDCConfigResponse, UpdateIDPRequest, @@ -677,6 +681,18 @@ export class AdminService { return this.grpcService.admin.reactivateIDP(req, null).then(resp => resp.toObject()); } + public addJWTIDP( + req: AddJWTIDPRequest, + ): Promise { + return this.grpcService.admin.addJWTIDP(req, null).then(resp => resp.toObject()); + } + + public updateIDPJWTConfig( + req: UpdateIDPJWTConfigRequest, + ): Promise { + return this.grpcService.admin.updateIDPJWTConfig(req, null).then(resp => resp.toObject()); + } + public listIAMMembers( limit: number, offset: number, diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index db65123d66..b730b0b029 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -40,6 +40,8 @@ import { AddOIDCAppResponse, AddOrgDomainRequest, AddOrgDomainResponse, + AddOrgJWTIDPRequest, + AddOrgJWTIDPResponse, AddOrgMemberRequest, AddOrgMemberResponse, AddOrgOIDCIDPRequest, @@ -372,6 +374,8 @@ import { UpdateMachineResponse, UpdateOIDCAppConfigRequest, UpdateOIDCAppConfigResponse, + UpdateOrgIDPJWTConfigRequest, + UpdateOrgIDPJWTConfigResponse, UpdateOrgIDPOIDCConfigRequest, UpdateOrgIDPOIDCConfigResponse, UpdateOrgIDPRequest, @@ -773,6 +777,18 @@ export class ManagementService { return this.grpcService.mgmt.reactivateOrgIDP(req, null).then(resp => resp.toObject()); } + public addOrgJWTIDP( + req: AddOrgJWTIDPRequest, + ): Promise { + return this.grpcService.mgmt.addOrgJWTIDP(req, null).then(resp => resp.toObject()); + } + + public updateOrgIDPJWTConfig( + req: UpdateOrgIDPJWTConfigRequest, + ): Promise { + return this.grpcService.mgmt.updateOrgIDPJWTConfig(req, null).then(resp => resp.toObject()); + } + public addHumanUser(req: AddHumanUserRequest): Promise { return this.grpcService.mgmt.addHumanUser(req, null).then(resp => resp.toObject()); } diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index b35654cc65..20469c5df2 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -757,6 +757,7 @@ "DESCRIPTION": "Definiere die Loginmethoden für Benutzer", "DESCRIPTIONCREATEADMIN": "Nutzer können sich mit den verfügbaren Idps authentifizieren.", "DESCRIPTIONCREATEMGMT": "Nutzer können sich mit den verfügbaren Idps authentifizieren. Achtung: Es kann zwischen System- und organisationsspezifischen Providern gewählt werden.", + "ADVANCED":"Erweitert", "SAVED": "Erfolgreich gespeichert." }, "PRIVACY_POLICY": { @@ -1039,6 +1040,7 @@ "MEMBERTITLE": "Berechtigte Manager der Organisation", "MEMBERDESC": "Dies sind die Manager der berechtigten Organisation. Wähle die Benutzer, die Zugriff zum Bearbeiten der jeweiligen Zugänge erhalten sollen.", "PROJECTNAME": "Projektname", + "GRANTEDORG":"Berechtigte Organisation", "RESOURCEOWNER": "Besitzer" }, "SHOWDETAIL": "Details anzeigen", @@ -1132,19 +1134,18 @@ "IDP": { "LIST": { "TITLE": "Identitäts Provider", - "DESCRIPTION": "Definieren Sie hier Ihre zusätzlichen Idps, die sie für die Authentifizierung in Ihren Organisationen verwenden können." + "DESCRIPTION": "Definieren Sie hier Ihre zusätzlichen Idps, die sie für die Authentifizierung in Ihren Organisationen verwenden können.", + "ACTIVETITLE":"Aktive Identitäts Provider" }, "CREATE": { "TITLE": "Neuer Identitäts Provider", - "DESCRIPTION": "Definieren Sie hier die Zugangsdaten des neuen Identitäts Providers" + "DESCRIPTION": "Wählen Sie einen der folgenden Typen von Identitäts Providern" }, "DETAIL": { "TITLE": "Identitäts Provider", "DESCRIPTION": "Generelle Konfiguration deines Identitäts Providers", - "OIDC": { - "TITLE": "OIDC Konfiguration", - "DESCRIPTION": "Geben Sie die korrekte OIDC Konfiguration Ihres Identity Providers an!" - } + "DATECREATED":"Erstellt", + "DATECHANGED":"Geändert" }, "OWNERTYPES": { "0": "unknown", @@ -1168,9 +1169,11 @@ "0": "kein Styling", "1": "Google" }, + "ADD":"Identity Provider hinzufügen", "AUTOREGISTER":"Automatische Registrierung", "AUTOREGISTER_DESC":"Wenn aktiviert und noch kein Account vorhanden ist, wird einer für den entsprechenden Benutzer erstellt.", "TYPE": "Typ", + "OWNER":"Besitzer", "ID": "ID", "NAME": "Name", "CONFIG": "Konfiguration", @@ -1181,15 +1184,27 @@ "CLIENTSECRET": "Client Secret", "IDPDISPLAYNAMMAPPING": "IDP Anzeigename Mapping", "USERNAMEMAPPING": "Username Mapping", + "DATES":"Datum", "CREATIONDATE": "Erstelldatum", "CHANGEDATE": "Letzte Änderung", "DEACTIVATE": "Deaktivieren", "ACTIVATE": "Aktivieren", "DELETE": "Löschen", - "DELETE_TITLE": "Idp löschen", + "DELETE_TITLE": "IDP löschen", "DELETE_DESCRIPTION": "Sie sind im Begriff einen Identity Provider zu löschen. Die daruch hervorgerufenen Änderungen sind unwiederruflich. Wollen Sie dies wirklich tun?", "DELETE_SELECTION_TITLE": "Identity Providers löschen", "DELETE_SELECTION_DESCRIPTION": "Sie sind im Begriff mehrere Identity Provider zu löschen. Die daruch hervorgerufenen Änderungen sind unwiederruflich. Wollen Sie dies wirklich tun?", + "OIDC": { + "TITLE":"OpenId Connect IDP", + "DESCRIPTION":"Geben Sie die Daten OIDC Identity Providers ein." + }, + "JWT": { + "TITLE":"JWT IDP", + "DESCRIPTION":"Geben Sie die Daten JWT Identity Providers ein. ", + "HEADERNAME":"Header Name", + "JWTENDPOINT":"JWT Endpoint", + "JWTKEYSENDPOINT":"JWT Keys Endpoint" + }, "TOAST": { "SAVED": "Erfolgreich gespeichert.", "REACTIVATED": "Idp reaktiviert.", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index e3d0106637..a43fcd94a6 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -759,6 +759,7 @@ "DESCRIPTION": "Define how Users can be authenticated and configure Identity Providers", "DESCRIPTIONCREATEADMIN": "Users can choose from the available identity providers below.", "DESCRIPTIONCREATEMGMT": "Users can choose from the available identity providers below. Note: You can use System-set providers as well as providers set for your organisation only.", + "ADVANCED":"Advanced", "SAVED": "Saved successfully!" }, "PRIVACY_POLICY": { @@ -1041,6 +1042,7 @@ "MEMBERTITLE": "Managers", "MEMBERDESC": "These are the managers of the granted organisation. Add users here who should gain access to edit the data of the project.", "PROJECTNAME": "Project Name", + "GRANTEDORG":"Granted Organisation", "RESOURCEOWNER": "Resource Owner" }, "SHOWDETAIL": "Show Details", @@ -1134,19 +1136,18 @@ "IDP": { "LIST": { "TITLE": "Identity Providers", - "DESCRIPTION": "Define additional Identity Providers, which can be used to authenticate in your organisations." + "DESCRIPTION": "Manage your Identity Provider configuration, which can then be activated in your Login Policy.", + "ACTIVETITLE":"Active Identity Providers" }, "CREATE": { "TITLE": "New Identity Provider", - "DESCRIPTION": "Configure the Endpoint of your new service provider." + "DESCRIPTION": "Choose one of the following Identity Provider types." }, "DETAIL": { "TITLE": "Identity Provider", "DESCRIPTION": "General Configuration of your identity provider.", - "OIDC": { - "TITLE": "OIDC Configuration", - "DESCRIPTION": "Provide the correct OIDC Configuration for your identity Provider below!" - } + "DATECREATED":"Created", + "DATECHANGED":"Changed" }, "OWNERTYPES": { "0": "unknown", @@ -1170,9 +1171,11 @@ "0": "No Styling", "1": "Google" }, + "ADD":"Add Identity Provider", "AUTOREGISTER":"Auto Register", "AUTOREGISTER_DESC":"If selected and no account exists yet, one will be created.", "TYPE": "Type", + "OWNER":"Owner", "ID": "ID", "NAME": "Name", "CONFIG": "Configuration", @@ -1183,6 +1186,7 @@ "CLIENTSECRET": "Client Secret", "IDPDISPLAYNAMMAPPING": "IDP Anzeigename Mapping", "USERNAMEMAPPING": "Username Mapping", + "DATES":"Dates", "CREATIONDATE": "Created At", "CHANGEDATE": "Last Modified", "DEACTIVATE": "Deactivate", @@ -1192,6 +1196,17 @@ "DELETE_DESCRIPTION": "You are about to delete an identity provider. The resulting changes are irrevocable. Do you really want to do this?", "DELETE_SELECTION_TITLE": "Delete Idp", "DELETE_SELECTION_DESCRIPTION": "You are about to delete an identity provider. The resulting changes are irrevocable. Do you really want to do this?", + "OIDC": { + "TITLE":"OpenId Connect IDP", + "DESCRIPTION":"Enter the data for OIDC Identity Provider." + }, + "JWT": { + "TITLE":"JWT IDP", + "DESCRIPTION":"Enter the data for JWT Identity Provider.", + "HEADERNAME":"Header Name", + "JWTENDPOINT":"JWT Endpoint", + "JWTKEYSENDPOINT":"JWT Keys Endpoint" + }, "TOAST": { "SAVED": "Successfully saved.", "REACTIVATED": "Idp reactivated.", diff --git a/console/src/assets/mdi/jwt.svg b/console/src/assets/mdi/jwt.svg new file mode 100644 index 0000000000..8a03e07532 --- /dev/null +++ b/console/src/assets/mdi/jwt.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/console/src/assets/mdi/openid.svg b/console/src/assets/mdi/openid.svg new file mode 100644 index 0000000000..8474024e5b --- /dev/null +++ b/console/src/assets/mdi/openid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index 2252452252..7f397f08ca 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -23,10 +23,12 @@ @import 'src/app/modules/onboarding/onboarding.component.scss'; @import 'src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss'; @import 'src/app/modules/info-row/info-row.component.scss'; +@import 'src/app/modules/idp-create/idp-type-radio/idp-type-radio.component.scss'; @mixin component-themes($theme) { @include avatar-theme($theme); @include app-type-radio-theme($theme); + @include idp-type-radio-theme($theme); @include app-auth-method-radio-theme($theme); @include card-theme($theme); @include table-theme($theme);