diff --git a/.gitignore b/.gitignore index 634ca2aee9..1fe6ace8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,5 @@ tmp/ console/src/app/proto/generated/ pkg/grpc/*/*.pb.* +pkg/grpc/*/mock/*.mock.go pkg/grpc/*/*.swagger.json \ No newline at end of file diff --git a/LICENSE b/LICENSE index 261eeb9e9f..a1ea99bb88 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2020 CAOS AG Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/cmd/zitadel/caos_local.sh b/cmd/zitadel/caos_local.sh index 817761d14d..3c323dcb64 100755 --- a/cmd/zitadel/caos_local.sh +++ b/cmd/zitadel/caos_local.sh @@ -67,7 +67,7 @@ export ZITADEL_SHORT_CACHE_SHARED_MAXAGE=15m export ZITADEL_CONSOLE_ENV_DIR=../../console/src/assets/ #Org -export ZITADEL_DEFAULT_DOMAIN=zitadel.ch +export ZITADEL_DEFAULT_DOMAIN=localhost #Setup diff --git a/cmd/zitadel/setup.yaml b/cmd/zitadel/setup.yaml index e761c2315d..df2cc6c8fb 100644 --- a/cmd/zitadel/setup.yaml +++ b/cmd/zitadel/setup.yaml @@ -98,4 +98,6 @@ SetUp: Step7: DefaultSecondFactor: 1 #SecondFactorTypeOTP Step8: - DefaultSecondFactor: 2 #SecondFactorTypeU2F \ No newline at end of file + DefaultSecondFactor: 2 #SecondFactorTypeU2F + Step9: + Passwordless: true \ No newline at end of file diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 88f5a5e1c9..8e92800397 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -49,11 +49,11 @@ AuthZ: Key: $CR_AUTHZ_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 Auth: - SearchLimit: 100 + SearchLimit: 1000 Domain: $ZITADEL_DEFAULT_DOMAIN Eventstore: ServiceName: 'authAPI' @@ -98,7 +98,7 @@ Auth: Key: $CR_AUTH_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 KeyConfig: Size: 2048 @@ -109,7 +109,7 @@ Auth: SigningKeyRotation: 10s Admin: - SearchLimit: 100 + SearchLimit: 1000 Domain: $ZITADEL_DEFAULT_DOMAIN Eventstore: ServiceName: 'Admin' @@ -142,11 +142,11 @@ Admin: Key: $CR_ADMINAPI_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 Mgmt: - SearchLimit: 100 + SearchLimit: 1000 Domain: $ZITADEL_DEFAULT_DOMAIN Eventstore: ServiceName: 'ManagementAPI' @@ -179,7 +179,7 @@ Mgmt: Key: $CR_MANAGEMENT_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 API: @@ -292,10 +292,6 @@ Notification: Key: $CR_NOTIFICATION_KEY Spooler: ConcurrentWorkers: 1 - BulkLimit: 100 + BulkLimit: 10000 FailureCountUntilSkip: 5 - Handlers: - Notification: - MinimumCycleDuration: 5s - User: - MinimumCycleDuration: 5s \ No newline at end of file + Handlers: \ No newline at end of file diff --git a/cmd/zitadel/system-defaults.yaml b/cmd/zitadel/system-defaults.yaml index 54a41bb068..1f22bf6185 100644 --- a/cmd/zitadel/system-defaults.yaml +++ b/cmd/zitadel/system-defaults.yaml @@ -126,7 +126,7 @@ SystemDefaults: Text: 'DomainClaimed.Text' ButtonText: 'DomainClaimed.ButtonText' WebAuthN: - ID: $ZITADEL_COOKIE_DOMAIN + ID: $ZITADEL_DEFAULT_DOMAIN OriginLogin: $ZITADEL_ACCOUNTS OriginConsole: $ZITADEL_CONSOLE DisplayName: ZITADEL \ No newline at end of file diff --git a/console/package-lock.json b/console/package-lock.json index af9edec84d..dcef7a1c80 100644 --- a/console/package-lock.json +++ b/console/package-lock.json @@ -4,26 +4,16 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@angular-devkit/architect": { - "version": "0.1100.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.3.tgz", - "integrity": "sha512-PF4PlGOIVW8eYAObP7B/vfk8TaHBEWzr2NOb+kHsIJJmx11iivkHRWzzQmTUlhwnRslCcg5ngwx0IBPDzNVeTg==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.0.3", - "rxjs": "6.6.3" - } - }, "@angular-devkit/build-angular": { - "version": "0.1100.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1100.3.tgz", - "integrity": "sha512-4Zng6I+QOPFx6uAdNxgKDYXeHYvowU2bANtsG0KFDxc5RN64FyqK2lZlTUPS6SejX14hOyj81/f4MIl25r7Xgg==", + "version": "0.1100.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1100.4.tgz", + "integrity": "sha512-qVkMbtOwlo+k8fvOBOwwfKWMx06k4I1qrdjpRYAoZCt3cdje4EBepSciLrHnTB+ouIqWxpEDfEXTYBS98tXbBg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1100.3", - "@angular-devkit/build-optimizer": "0.1100.3", - "@angular-devkit/build-webpack": "0.1100.3", - "@angular-devkit/core": "11.0.3", + "@angular-devkit/architect": "0.1100.4", + "@angular-devkit/build-optimizer": "0.1100.4", + "@angular-devkit/build-webpack": "0.1100.4", + "@angular-devkit/core": "11.0.4", "@babel/core": "7.12.3", "@babel/generator": "7.12.1", "@babel/plugin-transform-runtime": "7.12.1", @@ -31,7 +21,7 @@ "@babel/runtime": "7.12.1", "@babel/template": "7.10.4", "@jsdevtools/coverage-istanbul-loader": "3.0.5", - "@ngtools/webpack": "11.0.3", + "@ngtools/webpack": "11.0.4", "ansi-colors": "4.1.1", "autoprefixer": "9.8.6", "babel-loader": "8.1.0", @@ -77,7 +67,7 @@ "speed-measure-webpack-plugin": "1.3.3", "style-loader": "2.0.0", "stylus": "0.54.8", - "stylus-loader": "4.1.1", + "stylus-loader": "4.3.1", "terser": "5.3.7", "terser-webpack-plugin": "4.2.3", "text-table": "0.2.0", @@ -91,6 +81,29 @@ "worker-plugin": "5.0.0" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.1100.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.4.tgz", + "integrity": "sha512-hzTfcSUwM0jsSt9HvvSFyaoAhX9k73L7y4kmkghzIFhKhIKOp/7o3n7hAFwN/jWKKmVQpPKnYmqzm9H9OveaCQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.4", + "rxjs": "6.6.3" + } + }, + "@angular-devkit/core": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.4.tgz", + "integrity": "sha512-LgTvhZ3Ycz0QvNAH/zO1rpQQDn2JN8u9/Awy1gW/XeCC3FYmxeOj/2JCFzlKah3wJv16nMqro5WTppHt8Y++PA==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -245,9 +258,9 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/template": { @@ -262,29 +275,29 @@ } }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" }, "dependencies": { "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -298,9 +311,9 @@ } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -308,6 +321,18 @@ "to-fast-properties": "^2.0.0" } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -341,9 +366,9 @@ } }, "@angular-devkit/build-optimizer": { - "version": "0.1100.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.3.tgz", - "integrity": "sha512-ACvi4W8iDqWJOqHZA7LLI1lldbjrouANxOb1wxgFyr6krKypU6kDXOF2ZWNdrsgDvW0zwJPKeDNFzYyvqQpe/w==", + "version": "0.1100.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.4.tgz", + "integrity": "sha512-C05y4qMb05PWR7l1gZwRQKiB6KIDq+p72r8Yr6jm0UO6raOtMM72R8nHnioMnGJcFtZDEAYXEF+X7soI3MMlfw==", "dev": true, "requires": { "loader-utils": "2.0.0", @@ -362,29 +387,39 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.1100.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1100.3.tgz", - "integrity": "sha512-X26Vplgu8x88AMSaMOdR28XIENAHYIAW98HloFOMIuqAubZYeoxaNIYleVO5haowU3YOLsD/FijmRKNXWUnYXg==", + "version": "0.1100.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1100.4.tgz", + "integrity": "sha512-uxe8gNSej3KF1FgqNtJmuRDbbINh3yLtXanXhRxFQLUj8IiNR8IciIVvy6RfXC5gqxcWwy1cOefJLLnuN9AOxQ==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1100.3", - "@angular-devkit/core": "11.0.3", + "@angular-devkit/architect": "0.1100.4", + "@angular-devkit/core": "11.0.4", "rxjs": "6.6.3" - } - }, - "@angular-devkit/core": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.3.tgz", - "integrity": "sha512-gJRiBj0gWDR2VtIvLvwwc/GM2MZvg1xw69ZbBJ1VuUgDqPBHdC8q3UMW3B82wdhxK+RBYa7ZOJxtIVggaHkm9g==", - "dev": true, - "requires": { - "ajv": "6.12.6", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.3", - "source-map": "0.7.3" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.1100.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.4.tgz", + "integrity": "sha512-hzTfcSUwM0jsSt9HvvSFyaoAhX9k73L7y4kmkghzIFhKhIKOp/7o3n7hAFwN/jWKKmVQpPKnYmqzm9H9OveaCQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.4", + "rxjs": "6.6.3" + } + }, + "@angular-devkit/core": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.4.tgz", + "integrity": "sha512-LgTvhZ3Ycz0QvNAH/zO1rpQQDn2JN8u9/Awy1gW/XeCC3FYmxeOj/2JCFzlKah3wJv16nMqro5WTppHt8Y++PA==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -405,50 +440,6 @@ } } }, - "@angular-devkit/core": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.3.tgz", - "integrity": "sha512-gJRiBj0gWDR2VtIvLvwwc/GM2MZvg1xw69ZbBJ1VuUgDqPBHdC8q3UMW3B82wdhxK+RBYa7ZOJxtIVggaHkm9g==", - "dev": true, - "requires": { - "ajv": "6.12.6", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.3", - "source-map": "0.7.3" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "@angular-devkit/schematics": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.3.tgz", - "integrity": "sha512-VZnqgnnfyzyMluIDvGp+ZlDU2P9BnjrhacBOdqBS/jNQ7oxyE0AWrUApGXcejOJ13Z7pEf31E64P3bImcjwP+A==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.0.3", - "ora": "5.1.0", - "rxjs": "6.6.3" - } - }, "@angular/animations": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.0.0.tgz", @@ -467,16 +458,16 @@ } }, "@angular/cli": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.3.tgz", - "integrity": "sha512-ytYVvALJ1YRDZYoqNoUcE4SLyNcMyt2V+Youaasj+C43V0h1GzHZ4J6G4X9sKaaiNAGV4GKjB9r7jzCymaiq+A==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.4.tgz", + "integrity": "sha512-VkE/gx6P80EJHg13fG+gkZfd2DJmRaDAtnamcCGM4AThzoUN9XBdxc24uMLEzBb0/mJ4vpMK9+WTNIdMmzl+Tg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1100.3", - "@angular-devkit/core": "11.0.3", - "@angular-devkit/schematics": "11.0.3", - "@schematics/angular": "11.0.3", - "@schematics/update": "0.1100.3", + "@angular-devkit/architect": "0.1100.4", + "@angular-devkit/core": "11.0.4", + "@angular-devkit/schematics": "11.0.4", + "@schematics/angular": "11.0.4", + "@schematics/update": "0.1100.4", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "4.2.0", @@ -494,6 +485,52 @@ "uuid": "8.3.1" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.1100.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.4.tgz", + "integrity": "sha512-hzTfcSUwM0jsSt9HvvSFyaoAhX9k73L7y4kmkghzIFhKhIKOp/7o3n7hAFwN/jWKKmVQpPKnYmqzm9H9OveaCQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.4", + "rxjs": "6.6.3" + } + }, + "@angular-devkit/core": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.4.tgz", + "integrity": "sha512-LgTvhZ3Ycz0QvNAH/zO1rpQQDn2JN8u9/Awy1gW/XeCC3FYmxeOj/2JCFzlKah3wJv16nMqro5WTppHt8Y++PA==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.4.tgz", + "integrity": "sha512-fFC7qW9A1bFAZgpCfkezBA4WCRzfVFgOzwPpyt65rgSrzw0+EeHjcrUIcXlhyOXAFrTHtA9oLCfEeSjSx5HBEA==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.4", + "ora": "5.1.0", + "rxjs": "6.6.3" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "debug": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", @@ -527,6 +564,18 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true } } }, @@ -729,9 +778,9 @@ } }, "@angular/language-service": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-11.0.3.tgz", - "integrity": "sha512-9tCPCiFwXco3QJhVIAFBjUGD3RS3SBOoaUsNAB0mGTGT6xOgayo37Z5UuOOHKHdlEpTlCFVLnSVm8DMZ4ehYpw==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-11.0.4.tgz", + "integrity": "sha512-KtQxVSlZi3SwZEN4E56KHkNTFEYa3FPZfLJFm6WD1dSobFyMwJgvztO08GWSaT4S0ht0NNRD2IRt0XzBYuZkag==", "dev": true }, "@angular/material": { @@ -859,12 +908,25 @@ } }, "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", + "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.10" + }, + "dependencies": { + "@babel/types": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-builder-binary-assignment-operator-visitor": { @@ -890,14 +952,14 @@ }, "dependencies": { "browserslist": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.15.0.tgz", - "integrity": "sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.0.tgz", + "integrity": "sha512-/j6k8R0p3nxOC6kx5JGAxsnhc9ixaWJfYc+TNTzxg6+ARaESAvQGV7h0uNOB4t+pLQJZWzcrMxXOxjgsCj3dqQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001164", + "caniuse-lite": "^1.0.30001165", "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.612", + "electron-to-chromium": "^1.3.621", "escalade": "^3.1.1", "node-releases": "^1.1.67" } @@ -909,9 +971,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.616", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.616.tgz", - "integrity": "sha512-CI8L38UN2BEnqXw3/oRIQTmde0LiSeqWSRlPA42ZTYgJQ8fYenzAM2Z3ni+jtILTcrs5aiXZCGJ96Pm+3/yGyQ==", + "version": "1.3.625", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.625.tgz", + "integrity": "sha512-CsLk/r0C9dAzVPa9QF74HIXduxaucsaRfqiOYvIv2PRhvyC6EOqc/KbpgToQuDVgPf3sNAFZi3iBu4vpGOwGag==", "dev": true }, "escalade": { @@ -951,12 +1013,12 @@ } }, "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -994,23 +1056,23 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -1028,9 +1090,9 @@ } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1086,9 +1148,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1253,9 +1315,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1330,9 +1392,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1396,9 +1458,9 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/template": { @@ -1413,9 +1475,9 @@ }, "dependencies": { "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1759,9 +1821,9 @@ } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -1815,12 +1877,12 @@ } }, "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -1858,23 +1920,23 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -1892,9 +1954,9 @@ } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -2024,12 +2086,12 @@ } }, "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -2111,9 +2173,9 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/template": { @@ -2128,26 +2190,26 @@ } }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -2194,12 +2256,12 @@ } }, "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -2281,9 +2343,9 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/template": { @@ -2298,26 +2360,26 @@ } }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -2365,12 +2427,12 @@ } }, "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -2452,9 +2514,9 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/template": { @@ -2469,26 +2531,26 @@ } }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -2533,12 +2595,12 @@ } }, "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -2620,9 +2682,9 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/template": { @@ -2637,26 +2699,26 @@ } }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -2719,12 +2781,12 @@ } }, "@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", "dev": true, "requires": { - "@babel/types": "^7.12.5", + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -2771,32 +2833,32 @@ } }, "@babel/parser": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", - "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", "dev": true }, "@babel/traverse": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", - "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", + "@babel/generator": "^7.12.10", "@babel/helper-function-name": "^7.10.4", "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -2879,9 +2941,9 @@ } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -2929,9 +2991,9 @@ } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz", - "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", + "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" @@ -3040,9 +3102,9 @@ } }, "@babel/types": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", - "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", @@ -3181,14 +3243,47 @@ } }, "@ngtools/webpack": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.3.tgz", - "integrity": "sha512-TbWharROiFA88HOLvchtSwJfnFJEgn8HcdXRDX7EL2efDYC8UVZfXms58MCEQUk66ZjikNeIHPQml5rStSqPKQ==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.4.tgz", + "integrity": "sha512-MAV7inQmsMISTnDcXwyRX5oJZx8F7K/tZRLJciQwkM0DqZyq8fI9KDRwBcmYeQ+J0mSJV9LUVdExmpulpkywqw==", "dev": true, "requires": { - "@angular-devkit/core": "11.0.3", + "@angular-devkit/core": "11.0.4", "enhanced-resolve": "5.3.1", "webpack-sources": "2.0.1" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.4.tgz", + "integrity": "sha512-LgTvhZ3Ycz0QvNAH/zO1rpQQDn2JN8u9/Awy1gW/XeCC3FYmxeOj/2JCFzlKah3wJv16nMqro5WTppHt8Y++PA==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } } }, "@ngx-translate/core": { @@ -3305,24 +3400,68 @@ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, "@schematics/angular": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.0.3.tgz", - "integrity": "sha512-H+rWJOafl8OXrC/GMJihWYTFsR2K49tWM6AyJLBxja6qyZwwL184SLPvvkPsz3+LDs+fxfOzQ1K+sIpuZLqizw==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.0.4.tgz", + "integrity": "sha512-LwBD9TIoLy9XqqInJvlN4BHtPyJExyeorNiOp6rXb/wafuDbvZ+9kY9GWZTY1auVo5PNKqErfxr74ydA3FFb9g==", "dev": true, "requires": { - "@angular-devkit/core": "11.0.3", - "@angular-devkit/schematics": "11.0.3", + "@angular-devkit/core": "11.0.4", + "@angular-devkit/schematics": "11.0.4", "jsonc-parser": "2.3.1" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.4.tgz", + "integrity": "sha512-LgTvhZ3Ycz0QvNAH/zO1rpQQDn2JN8u9/Awy1gW/XeCC3FYmxeOj/2JCFzlKah3wJv16nMqro5WTppHt8Y++PA==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.4.tgz", + "integrity": "sha512-fFC7qW9A1bFAZgpCfkezBA4WCRzfVFgOzwPpyt65rgSrzw0+EeHjcrUIcXlhyOXAFrTHtA9oLCfEeSjSx5HBEA==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.4", + "ora": "5.1.0", + "rxjs": "6.6.3" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } } }, "@schematics/update": { - "version": "0.1100.3", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1100.3.tgz", - "integrity": "sha512-Hp+RVuVLYkJhGYzLY3kwSqk8nh9zC9F9AAR4QuwSXQv+pQxRbv48fdU3iVuKu/mVhz17RZJhTNoA/2uNMeGH2g==", + "version": "0.1100.4", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1100.4.tgz", + "integrity": "sha512-YwFtgxCQQkYC89IC7dfshyGr0roE6bpp5HgpQLdS/AOjHeZKo7/SPdM0W4ddB+Fml1Fo6v4eFG/Ia9oR7qNv1A==", "dev": true, "requires": { - "@angular-devkit/core": "11.0.3", - "@angular-devkit/schematics": "11.0.3", + "@angular-devkit/core": "11.0.4", + "@angular-devkit/schematics": "11.0.4", "@yarnpkg/lockfile": "1.1.0", "ini": "1.3.5", "npm-package-arg": "^8.0.0", @@ -3331,11 +3470,53 @@ "semver-intersect": "1.4.0" }, "dependencies": { + "@angular-devkit/core": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.4.tgz", + "integrity": "sha512-LgTvhZ3Ycz0QvNAH/zO1rpQQDn2JN8u9/Awy1gW/XeCC3FYmxeOj/2JCFzlKah3wJv16nMqro5WTppHt8Y++PA==", + "dev": true, + "requires": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.4.tgz", + "integrity": "sha512-fFC7qW9A1bFAZgpCfkezBA4WCRzfVFgOzwPpyt65rgSrzw0+EeHjcrUIcXlhyOXAFrTHtA9oLCfEeSjSx5HBEA==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.0.4", + "ora": "5.1.0", + "rxjs": "6.6.3" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, @@ -3441,9 +3622,9 @@ "dev": true }, "@types/node": { - "version": "14.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz", - "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==" + "version": "14.14.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", + "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -5481,14 +5662,14 @@ }, "dependencies": { "browserslist": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.15.0.tgz", - "integrity": "sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.0.tgz", + "integrity": "sha512-/j6k8R0p3nxOC6kx5JGAxsnhc9ixaWJfYc+TNTzxg6+ARaESAvQGV7h0uNOB4t+pLQJZWzcrMxXOxjgsCj3dqQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001164", + "caniuse-lite": "^1.0.30001165", "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.612", + "electron-to-chromium": "^1.3.621", "escalade": "^3.1.1", "node-releases": "^1.1.67" } @@ -5500,9 +5681,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.616", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.616.tgz", - "integrity": "sha512-CI8L38UN2BEnqXw3/oRIQTmde0LiSeqWSRlPA42ZTYgJQ8fYenzAM2Z3ni+jtILTcrs5aiXZCGJ96Pm+3/yGyQ==", + "version": "1.3.625", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.625.tgz", + "integrity": "sha512-CsLk/r0C9dAzVPa9QF74HIXduxaucsaRfqiOYvIv2PRhvyC6EOqc/KbpgToQuDVgPf3sNAFZi3iBu4vpGOwGag==", "dev": true }, "escalade": { @@ -8067,9 +8248,9 @@ "dev": true }, "html-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", - "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.3.tgz", + "integrity": "sha512-/VulV3SYni1taM7a4RMdceqzJWR39gpZHjBwUnsCFKWV/GJkD14CJ5F7eWcZozmHJK0/f/H5U3b3SiPkuvxMgg==", "dev": true }, "html-escaper": { @@ -15450,9 +15631,9 @@ } }, "stylus-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.1.1.tgz", - "integrity": "sha512-Vnm7J/nIs/P6swIrdwJW/dflhsCOiFmb1U3PeQ6phRtg1soPLN4uKnnL7AtGIJDe173elbtYIXVzmCyF493CfA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.3.1.tgz", + "integrity": "sha512-apDYJEM5ZpOAWbWInWcsbtI8gHNr/XYVcSY/tWqOUPt7M5tqhtwXVsAkgyiVjhuvw2Yrjq474a9H+g4d047Ebw==", "dev": true, "requires": { "fast-glob": "^3.2.4", @@ -15910,9 +16091,9 @@ "dev": true }, "ts-node": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.0.tgz", - "integrity": "sha512-0yqcL4sgruCvM+w64LiAfNJo6+lHfCYc5Ajj4yiLNkJ9oZ2HWaa+Kso7htYOOxVQ7+csAjdUjffOe9PIqC4pMg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", "dev": true, "requires": { "arg": "^4.1.0", @@ -16415,9 +16596,9 @@ "dev": true }, "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.2.0", diff --git a/console/package.json b/console/package.json index 07bdb78cba..a53f385b39 100644 --- a/console/package.json +++ b/console/package.json @@ -40,17 +40,17 @@ "rxjs": "~6.6.3", "ts-protoc-gen": "^0.13.0", "tslib": "^2.0.0", - "uuid": "^8.3.1", + "uuid": "^8.3.2", "zone.js": "~0.11.3" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.1100.3", - "@angular/cli": "~11.0.3", + "@angular-devkit/build-angular": "~0.1100.4", + "@angular/cli": "~11.0.4", "@angular/compiler-cli": "~11.0.0", "@types/jasmine": "~3.6.2", - "@angular/language-service": "~11.0.3", + "@angular/language-service": "~11.0.4", "@types/jasminewd2": "~2.0.3", - "@types/node": "^14.14.10", + "@types/node": "^14.14.13", "codelyzer": "^6.0.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~6.0.0", @@ -64,7 +64,7 @@ "stylelint": "^13.8.0", "stylelint-config-standard": "^20.0.0", "stylelint-scss": "^3.18.0", - "ts-node": "~9.1.0", + "ts-node": "~9.1.1", "tslint": "~6.1.3", "typescript": "^4.0.5" } diff --git a/console/src/app/modules/changes/changes.component.ts b/console/src/app/modules/changes/changes.component.ts index eeee5de055..58001fd7ae 100644 --- a/console/src/app/modules/changes/changes.component.ts +++ b/console/src/app/modules/changes/changes.component.ts @@ -42,7 +42,6 @@ export class ChangesComponent implements OnInit, OnDestroy { this.init(); if (this.refresh) { this.refresh.pipe(takeUntil(this.destroyed$), debounceTime(2000)).subscribe(() => { - console.log('asdf'); this.init(); }); } diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html new file mode 100644 index 0000000000..8d56ad9489 --- /dev/null +++ b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.html @@ -0,0 +1,19 @@ +

{{data.title | translate}}

+
+

{{data.desc | translate}}

+ + + {{'MFA.TYPE' | translate}} + + + {{(data.componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}} + + + +
+
+ + +
\ No newline at end of file diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss new file mode 100644 index 0000000000..2a10cfe140 --- /dev/null +++ b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.scss @@ -0,0 +1,22 @@ +.title { + font-size: 1.5rem; +} + +.desc { + font-size: 14px; + color: var(--grey); +} + +.form-field { + width: 100%; +} + +.action { + display: flex; + justify-content: flex-end; + + button { + margin-left: .5rem; + border-radius: .5rem; + } +} diff --git a/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts new file mode 100644 index 0000000000..befb157abf --- /dev/null +++ b/console/src/app/modules/mfa-table/dialog-add-type/dialog-add-type.component.ts @@ -0,0 +1,33 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +import { MultiFactorType as AdminMultiFactorType } from 'src/app/proto/generated/admin_pb'; +import { MultiFactorType as MgmtMultiFactorType } from 'src/app/proto/generated/management_pb'; + +enum LoginMethodComponentType { + MultiFactor = 1, + SecondFactor = 2, +} + +@Component({ + selector: 'app-dialog-add-type', + templateUrl: './dialog-add-type.component.html', + styleUrls: ['./dialog-add-type.component.scss'], +}) +export class DialogAddTypeComponent { + public LoginMethodComponentType: any = LoginMethodComponentType; + public newMfaType!: AdminMultiFactorType | MgmtMultiFactorType; + public availableMfaTypes: Array = []; + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any) { + this.availableMfaTypes = data.types; + } + + public closeDialog(): void { + this.dialogRef.close(); + } + + public closeDialogWithCode(): void { + this.dialogRef.close(this.newMfaType); + } +} diff --git a/console/src/app/modules/mfa-table/mfa-table.component.html b/console/src/app/modules/mfa-table/mfa-table.component.html new file mode 100644 index 0000000000..3d45e4c2f5 --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.html @@ -0,0 +1,15 @@ +
+ +
+
+
+ + {{(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 new file mode 100644 index 0000000000..f0fe7e80a7 --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.scss @@ -0,0 +1,42 @@ + +.t .sp_wrapper { + display: block; +} + +.mfa-list { + display: flex; + flex-wrap: wrap; + margin: 0 -.5rem; + + .mfa { + border: 1px solid var(--grey); + border-radius: .5rem; + display: grid; + align-items: center; + justify-content: center; + margin: .5rem; + padding: 10px; + cursor: pointer; + position: relative; + min-height: 70px; + min-width: 150px; + + .rm { + position: absolute; + top: 0; + left: 0; + transform: translateX(-50%) translateY(-50%); + cursor: pointer; + + &[disabled] { + display: none; + } + } + + &:not(.disabled) { + &:hover { + background-color: #ffffff10; + } + } + } +} diff --git a/console/src/app/modules/mfa-table/mfa-table.component.spec.ts b/console/src/app/modules/mfa-table/mfa-table.component.spec.ts new file mode 100644 index 0000000000..24992ccaff --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { MfaTableComponent } from './mfa-table.component'; + +describe('MfaTableComponent', () => { + let component: MfaTableComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [MfaTableComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MfaTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/mfa-table/mfa-table.component.ts b/console/src/app/modules/mfa-table/mfa-table.component.ts new file mode 100644 index 0000000000..a8c425b705 --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.component.ts @@ -0,0 +1,232 @@ +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 { + MultiFactor as AdminMultiFactor, + MultiFactorType as AdminMultiFactorType, + SecondFactor as AdminSecondFactor, + SecondFactorType as AdminSecondFactorType, +} from 'src/app/proto/generated/admin_pb'; +import { + MultiFactor as MgmtMultiFactor, + MultiFactorType as MgmtMultiFactorType, + SecondFactor as MgmtSecondFactor, + SecondFactorType as MgmtSecondFactorType, +} from 'src/app/proto/generated/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 { 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: MgmtMultiFactorType | AdminMultiFactorType | MgmtSecondFactorType | AdminSecondFactorType): 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 MgmtMultiFactor(); + req.setMultiFactor(type as MgmtMultiFactorType); + (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 MgmtSecondFactor(); + req.setSecondFactor(type as MgmtSecondFactorType); + (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 AdminMultiFactor(); + req.setMultiFactor(type as AdminMultiFactorType); + (this.service as AdminService).RemoveMultiFactorFromDefaultLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new AdminSecondFactor(); + req.setSecondFactor(type as AdminSecondFactorType); + (this.service as AdminService).RemoveSecondFactorFromDefaultLoginPolicy(req).then(() => { + this.toast.showInfo('MFA.TOAST.DELETED', true); + this.refreshPageAfterTimout(2000); + }); + } + } + } + }); + } + + public addMfa(): void { + + let selection: any[] = []; + + if (this.componentType === LoginMethodComponentType.MultiFactor) { + selection = this.serviceType === PolicyComponentServiceType.MGMT ? + [MgmtMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] : + this.serviceType === PolicyComponentServiceType.ADMIN ? + [AdminMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] : + []; + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + selection = this.serviceType === PolicyComponentServiceType.MGMT ? + [MgmtSecondFactorType.SECONDFACTORTYPE_U2F, MgmtSecondFactorType.SECONDFACTORTYPE_OTP] : + this.serviceType === PolicyComponentServiceType.ADMIN ? + [AdminSecondFactorType.SECONDFACTORTYPE_OTP, AdminSecondFactorType.SECONDFACTORTYPE_U2F] : + []; + } + + 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: AdminMultiFactorType | MgmtMultiFactorType | + AdminSecondFactorType | MgmtSecondFactorType) => { + if (mfaType) { + if (this.serviceType === PolicyComponentServiceType.MGMT) { + if (this.componentType === LoginMethodComponentType.MultiFactor) { + const req = new MgmtMultiFactor(); + req.setMultiFactor(mfaType as MgmtMultiFactorType); + (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 MgmtSecondFactor(); + req.setSecondFactor(mfaType as MgmtSecondFactorType); + (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 AdminMultiFactor(); + req.setMultiFactor(mfaType as AdminMultiFactorType); + (this.service as AdminService).addMultiFactorToDefaultLoginPolicy(req).then(() => { + this.refreshPageAfterTimout(2000); + }).catch(error => { + this.toast.showError(error); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + const req = new AdminSecondFactor(); + req.setSecondFactor(mfaType as AdminSecondFactorType); + (this.service as AdminService).AddSecondFactorToDefaultLoginPolicy(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).GetLoginPolicyMultiFactors().then(resp => { + this.mfas = resp.toObject().multiFactorsList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + (this.service as ManagementService).GetLoginPolicySecondFactors().then(resp => { + this.mfas = resp.toObject().secondFactorsList; + 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).getDefaultLoginPolicyMultiFactors().then(resp => { + this.mfas = resp.toObject().multiFactorsList; + this.loadingSubject.next(false); + }).catch(error => { + this.toast.showError(error); + this.loadingSubject.next(false); + }); + } else if (this.componentType === LoginMethodComponentType.SecondFactor) { + (this.service as AdminService).GetDefaultLoginPolicySecondFactors().then(resp => { + this.mfas = resp.toObject().secondFactorsList; + 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 new file mode 100644 index 0000000000..8db0c2cf9d --- /dev/null +++ b/console/src/app/modules/mfa-table/mfa-table.module.ts @@ -0,0 +1,44 @@ +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'; + +@NgModule({ + declarations: [MfaTableComponent, DialogAddTypeComponent], + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatIconModule, + InputModule, + MatSelectModule, + MatTooltipModule, + TranslateModule, + TimestampToDatePipeModule, + HasRoleModule, + MatProgressSpinnerModule, + ], + exports: [ + MfaTableComponent, + ], +}) +export class MfaTableModule { } 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 dcd843ab65..e88404bf39 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 @@ -44,8 +44,46 @@

{{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}}

+
+ + {{'POLICY.DATA.FORCEMFA' | translate}} + +

{{'POLICY.DATA.FORCEMFA_DESC' | translate}}

+
+
+ + {{'LOGINPOLICY.PASSWORDLESS' | translate}} + + + {{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}} + + + +
+ + +
+ + +

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

+

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

+ + + +

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

+

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

+ + +
+

{{'LOGINPOLICY.IDPS' | translate}}

@@ -63,9 +101,6 @@
- -

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

{{ 'IDP.LIST.DESCRIPTION' | 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 ea690233c2..5d2b27fbc2 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,6 +37,11 @@ width: 100%; } +.subdesc { + color: var(--grey); + font-size: 14px; +} + .idps { display: flex; margin: 0 -.5rem; @@ -93,3 +98,10 @@ } } } + +.divider { + width: 100%; + height: 1px; + background-color: var(--grey); + margin: 1rem 0; +} 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 119a251433..9bb05c39a5 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 @@ -3,18 +3,23 @@ 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 { DefaultLoginPolicy, + DefaultLoginPolicyRequest, DefaultLoginPolicyView, IdpProviderView as AdminIdpProviderView, IdpView as AdminIdpView, + PasswordlessType as AdminPasswordlessType, } from 'src/app/proto/generated/admin_pb'; import { IdpProviderType, IdpProviderView as MgmtIdpProviderView, IdpView as MgmtIdpView, LoginPolicy, + LoginPolicyRequest, LoginPolicyView, + PasswordlessType as MgmtPasswordlessType, } from 'src/app/proto/generated/management_pb'; import { AdminService } from 'src/app/services/admin.service'; import { ManagementService } from 'src/app/services/mgmt.service'; @@ -29,6 +34,8 @@ import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component styleUrls: ['./login-policy.component.scss'], }) export class LoginPolicyComponent implements OnDestroy { + public LoginMethodComponentType: any = LoginMethodComponentType; + public passwordlessTypes: Array = []; public loginData!: LoginPolicyView.AsObject | DefaultLoginPolicyView.AsObject; private sub: Subscription = new Subscription(); @@ -50,9 +57,13 @@ export class LoginPolicyComponent implements OnDestroy { switch (this.serviceType) { case PolicyComponentServiceType.MGMT: this.service = this.injector.get(ManagementService as Type); + this.passwordlessTypes = [MgmtPasswordlessType.PASSWORDLESSTYPE_ALLOWED, + MgmtPasswordlessType.PASSWORDLESSTYPE_NOT_ALLOWED]; break; case PolicyComponentServiceType.ADMIN: this.service = this.injector.get(AdminService as Type); + this.passwordlessTypes = [AdminPasswordlessType.PASSWORDLESSTYPE_ALLOWED, + AdminPasswordlessType.PASSWORDLESSTYPE_NOT_ALLOWED]; break; } @@ -108,20 +119,28 @@ export class LoginPolicyComponent implements OnDestroy { Promise { switch (this.serviceType) { case PolicyComponentServiceType.MGMT: - const mgmtreq = new LoginPolicy(); + const mgmtreq = new LoginPolicyRequest(); mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp); mgmtreq.setAllowRegister(this.loginData.allowRegister); mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); + mgmtreq.setForceMfa(this.loginData.forceMfa); + mgmtreq.setPasswordlessType(this.loginData.passwordlessType); + // console.log(mgmtreq.toObject()); if ((this.loginData as LoginPolicyView.AsObject).pb_default) { return (this.service as ManagementService).CreateLoginPolicy(mgmtreq); } else { return (this.service as ManagementService).UpdateLoginPolicy(mgmtreq); } case PolicyComponentServiceType.ADMIN: - const adminreq = new DefaultLoginPolicy(); + const adminreq = new DefaultLoginPolicyRequest(); adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp); adminreq.setAllowRegister(this.loginData.allowRegister); adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); + adminreq.setForceMfa(this.loginData.forceMfa); + adminreq.setPasswordlessType(this.loginData.passwordlessType); + + // console.log(adminreq.toObject()); + return (this.service as AdminService).UpdateDefaultLoginPolicy(adminreq); } } 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 8ea3fe5a67..a53e209629 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 @@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatSelectModule } from '@angular/material/select'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; @@ -12,6 +13,7 @@ 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 { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module'; @@ -36,7 +38,9 @@ import { LoginPolicyComponent } from './login-policy.component'; DetailLayoutModule, AddIdpDialogModule, IdpTableModule, + MfaTableModule, MatProgressSpinnerModule, + MatSelectModule, ], }) export class LoginPolicyModule { } diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index 726a157dce..ab1aee6ef9 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -127,6 +127,22 @@ {{'APP.OIDC.IDTOKENROLEASSERTION_DESCRIPTION' | translate}} + + {{'APP.OIDC.IDTOKENUSERINFOASSERTION' | translate}} + + {{'APP.OIDC.IDTOKENUSERINFOASSERTION_DESCRIPTION' | translate}} + + +

ClockSkew

+ + + + {{'APP.OIDC.CLOCKSKEW' | translate}} + + +

{{'APP.OIDC.REDIRECTSECTIONTITLE' | translate}}

diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss b/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss index 615b10b2fe..db7946ac71 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.scss @@ -128,6 +128,17 @@ margin-right: .5rem; } + .clockskew-title { + font-size: 14px; + color: var(--grey); + margin: 1rem .5rem 0 .5rem; + } + + .clockskew-slider { + width: 100%; + margin: 0 .5rem; + } + .desc { color: var(--grey); font-size: 14px; diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts b/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts index 719a4e4c8c..2e4cf4a1cb 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts @@ -6,6 +6,7 @@ import { MatButtonToggleChange } from '@angular/material/button-toggle'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; +import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; import { Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; import { ChangeType } from 'src/app/modules/changes/changes.component'; @@ -15,6 +16,7 @@ import { OIDCApplicationType, OIDCAuthMethodType, OIDCConfig, + OIDCConfigUpdate, OIDCGrantType, OIDCResponseType, OIDCTokenType, @@ -116,9 +118,15 @@ export class AppDetailComponent implements OnInit, OnDestroy { accessTokenType: [{ value: '', disabled: true }], accessTokenRoleAssertion: [{ value: false, disabled: true }], idTokenRoleAssertion: [{ value: false, disabled: true }], + idTokenUserinfoAssertion: [{ value: false, disabled: true }], + clockSkewSeconds: [{ value: 0, disabled: true }], }); } + public formatClockSkewLabel(seconds: number): string { + return seconds + 's'; + } + public ngOnInit(): void { this.subscription = this.route.params.subscribe(params => this.getData(params)); } @@ -132,11 +140,12 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.mgmtService.GetIam().then(iam => { this.isZitadel = iam.toObject().iamProjectId === this.projectId; }); - this.authService.isAllowed(['project.app.write$', 'project.app.write:' + id]).pipe(take(1)).subscribe((allowed) => { + this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid]).pipe(take(1)).subscribe((allowed) => { this.canWrite = allowed; this.mgmtService.GetApplicationById(projectid, id).then(app => { this.app = app.toObject(); this.appNameForm.patchValue(this.app); + console.log(this.app); if (allowed) { this.appNameForm.enable(); this.appForm.enable(); @@ -150,6 +159,11 @@ export class AppDetailComponent implements OnInit, OnDestroy { if (this.app.oidcConfig?.postLogoutRedirectUrisList) { this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList; } + if (this.app.oidcConfig?.clockSkew) { + const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000; + console.log(inSecs); + this.appForm.controls['clockSkewSeconds'].setValue(inSecs); + } if (this.app.oidcConfig) { this.appForm.patchValue(this.app.oidcConfig); } @@ -159,8 +173,6 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.errorMessage = error.message; }); }); - - this.docs = (await this.mgmtService.GetZitadelDocs()).toObject(); } @@ -247,9 +259,32 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.app.oidcConfig.accessTokenType = this.accessTokenType?.value; this.app.oidcConfig.accessTokenRoleAssertion = this.accessTokenRoleAssertion?.value; this.app.oidcConfig.idTokenRoleAssertion = this.idTokenRoleAssertion?.value; + this.app.oidcConfig.idTokenUserinfoAssertion = this.idTokenUserinfoAssertion?.value; + + const req = new OIDCConfigUpdate(); + req.setProjectId(this.projectId); + req.setApplicationId(this.app.id); + req.setRedirectUrisList(this.app.oidcConfig.redirectUrisList); + req.setResponseTypesList(this.app.oidcConfig.responseTypesList); + req.setAuthMethodType(this.app.oidcConfig.authMethodType); + req.setPostLogoutRedirectUrisList(this.app.oidcConfig.postLogoutRedirectUrisList); + req.setGrantTypesList(this.app.oidcConfig.grantTypesList); + req.setApplicationType(this.app.oidcConfig.applicationType); + req.setDevMode(this.app.oidcConfig.devMode); + req.setAccessTokenType(this.app.oidcConfig.accessTokenType); + req.setAccessTokenRoleAssertion(this.app.oidcConfig.accessTokenRoleAssertion); + req.setIdTokenRoleAssertion(this.app.oidcConfig.idTokenRoleAssertion); + req.setIdTokenUserinfoAssertion(this.app.oidcConfig.idTokenUserinfoAssertion); + if (this.clockSkewSeconds?.value) { + const dur = new Duration(); + dur.setSeconds(Math.floor(this.clockSkewSeconds?.value)); + dur.setNanos((Math.floor(this.clockSkewSeconds?.value % 1) * 10000)); + req.setClockSkew(dur); + } + console.log(req.toObject()); this.mgmtService - .UpdateOIDCAppConfig(this.projectId, this.app.id, this.app.oidcConfig) + .UpdateOIDCAppConfig(req) .then(() => { this.toast.showInfo('APP.TOAST.OIDCUPDATED', true); }) @@ -319,4 +354,12 @@ export class AppDetailComponent implements OnInit, OnDestroy { public get accessTokenRoleAssertion(): AbstractControl | null { return this.appForm.get('accessTokenRoleAssertion'); } + + public get idTokenUserinfoAssertion(): AbstractControl | null { + return this.appForm.get('idTokenUserinfoAssertion'); + } + + public get clockSkewSeconds(): AbstractControl | null { + return this.appForm.get('clockSkewSeconds'); + } } diff --git a/console/src/app/pages/projects/apps/apps.module.ts b/console/src/app/pages/projects/apps/apps.module.ts index 81559e7168..ff7fd7c0af 100644 --- a/console/src/app/pages/projects/apps/apps.module.ts +++ b/console/src/app/pages/projects/apps/apps.module.ts @@ -13,6 +13,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatRadioModule } from '@angular/material/radio'; import { MatSelectModule } from '@angular/material/select'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatSliderModule } from '@angular/material/slider'; import { MatStepperModule } from '@angular/material/stepper'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; @@ -61,6 +62,7 @@ import { AppsRoutingModule } from './apps-routing.module'; MatSlideToggleModule, InputModule, MetaLayoutModule, + MatSliderModule, ChangesModule, InfoSectionModule, ], diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.html new file mode 100644 index 0000000000..d5e79db8b1 --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.html @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + +
{{ 'USER.PASSWORDLESS.NAME' | translate }} + {{ mfa?.name }} + + {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} + {{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}} + + + {{ 'USER.PASSWORDLESS.TABLEACTIONS' | translate }} + +
+
+
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.scss b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.scss new file mode 100644 index 0000000000..3f9facae7c --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.scss @@ -0,0 +1,41 @@ + +.add-row { + display: flex; + margin: -.5rem; + flex-wrap: wrap; + + .button { + margin: .5rem; + margin-top: 1rem; + + .icon { + margin-right: .5rem; + } + } +} + +.centered { + display: flex; + align-items: center; + + i { + margin-left: 1rem; + color: var(--color-main); + } +} + +.table { + width: 100%; + + td, + th { + &:first-child { + padding-left: 0; + padding-right: 1rem; + } + + &:last-child { + padding-right: 0; + } + } +} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.spec.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.spec.ts new file mode 100644 index 0000000000..fbee4d4c7f --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { AuthPasswordlessComponent } from './auth-passwordless.component'; + +describe('AuthPasswordlessComponent', () => { + let component: AuthPasswordlessComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AuthPasswordlessComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AuthPasswordlessComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts new file mode 100644 index 0000000000..eefdb0efe1 --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts @@ -0,0 +1,121 @@ +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSort } from '@angular/material/sort'; +import { MatTable, MatTableDataSource } from '@angular/material/table'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; +import { MFAState, WebAuthNResponse, WebAuthNToken } from 'src/app/proto/generated/auth_pb'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { ToastService } from 'src/app/services/toast.service'; + +import { _base64ToArrayBuffer } from '../../u2f-util'; +import { DialogU2FComponent, U2FComponentDestination } from '../dialog-u2f/dialog-u2f.component'; + +export interface WebAuthNOptions { + challenge: string; + rp: { name: string, id: string; }; + user: { name: string, id: string, displayName: string; }; + pubKeyCredParams: any; + authenticatorSelection: { userVerification: string; }; + timeout: number; + attestation: string; +} + +@Component({ + selector: 'app-auth-passwordless', + templateUrl: './auth-passwordless.component.html', + styleUrls: ['./auth-passwordless.component.scss'], +}) +export class AuthPasswordlessComponent implements OnInit, OnDestroy { + public displayedColumns: string[] = ['name', 'state', 'actions']; + private loadingSubject: BehaviorSubject = new BehaviorSubject(false); + public loading$: Observable = this.loadingSubject.asObservable(); + + @ViewChild(MatTable) public table!: MatTable; + @ViewChild(MatSort) public sort!: MatSort; + public dataSource!: MatTableDataSource; + + public MFAState: any = MFAState; + public error: string = ''; + + constructor(private service: GrpcAuthService, + private toast: ToastService, + private dialog: MatDialog) { } + + public ngOnInit(): void { + this.getPasswordless(); + } + + public ngOnDestroy(): void { + this.loadingSubject.complete(); + } + + public addPasswordless(): void { + this.service.AddMyPasswordless().then((u2fresp) => { + const webauthn: WebAuthNResponse.AsObject = u2fresp.toObject(); + const credOptions: CredentialCreationOptions = JSON.parse(atob(webauthn.publicKey as string)); + + if (credOptions.publicKey?.challenge) { + credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any); + credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any); + if (credOptions.publicKey.excludeCredentials) { + credOptions.publicKey.excludeCredentials.map(cred => { + cred.id = _base64ToArrayBuffer(cred.id as any); + return cred; + }); + } + console.log(credOptions); + const dialogRef = this.dialog.open(DialogU2FComponent, { + width: '400px', + data: { + credOptions, + type: U2FComponentDestination.PASSWORDLESS, + }, + }); + + dialogRef.afterClosed().subscribe(done => { + if (done) { + this.getPasswordless(); + } else { + this.getPasswordless(); + } + }); + } + + }, error => { + this.toast.showError(error); + }); + } + + public getPasswordless(): void { + this.service.GetMyPasswordless().then(passwordless => { + this.dataSource = new MatTableDataSource(passwordless.toObject().tokensList); + this.dataSource.sort = this.sort; + }).catch(error => { + this.error = error.message; + }); + } + + public deletePasswordless(id?: string): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'USER.PASSWORDLESS.DIALOG.DELETE_TITLE', + descriptionKey: 'USER.PASSWORDLESS.DIALOG.DELETE_DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp && id) { + this.service.RemoveMyPasswordless(id).then(() => { + this.toast.showInfo('USER.TOAST.PASSWORDLESSREMOVED', true); + this.getPasswordless(); + }).catch(error => { + this.toast.showError(error); + }); + } + }); + } +} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html index 4f65a79667..e28affdae5 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html @@ -51,6 +51,8 @@ + + - + + + + + + @@ -35,6 +43,11 @@ matTooltip="{{'ACTIONS.NEW' | translate}}"> {{'USER.MFA.OTP' | translate}} +
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss index 411f391e2f..3f9facae7c 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.scss @@ -2,6 +2,7 @@ .add-row { display: flex; margin: -.5rem; + flex-wrap: wrap; .button { margin: .5rem; diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts index ea9baa7253..972d7172ae 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts @@ -4,11 +4,23 @@ import { MatSort } from '@angular/material/sort'; import { MatTable, MatTableDataSource } from '@angular/material/table'; import { BehaviorSubject, Observable } from 'rxjs'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; -import { MfaOtpResponse, MFAState, MfaType, MultiFactor } from 'src/app/proto/generated/auth_pb'; +import { MfaOtpResponse, MFAState, MfaType, MultiFactor, WebAuthNResponse } from 'src/app/proto/generated/auth_pb'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ToastService } from 'src/app/services/toast.service'; +import { _base64ToArrayBuffer } from '../../u2f-util'; import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component'; +import { DialogU2FComponent, U2FComponentDestination } from '../dialog-u2f/dialog-u2f.component'; + +export interface WebAuthNOptions { + challenge: string; + rp: { name: string, id: string; }; + user: { name: string, id: string, displayName: string; }; + pubKeyCredParams: any; + authenticatorSelection: { userVerification: string; }; + timeout: number; + attestation: string; +} @Component({ selector: 'app-auth-user-mfa', @@ -16,7 +28,7 @@ import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component'; styleUrls: ['./auth-user-mfa.component.scss'], }) export class AuthUserMfaComponent implements OnInit, OnDestroy { - public displayedColumns: string[] = ['type', 'state', 'actions']; + public displayedColumns: string[] = ['type', 'attr', 'state', 'actions']; private loadingSubject: BehaviorSubject = new BehaviorSubject(false); public loading$: Observable = this.loadingSubject.asObservable(); @@ -29,10 +41,13 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { public error: string = ''; public otpAvailable: boolean = false; - constructor(private service: GrpcAuthService, private toast: ToastService, private dialog: MatDialog) { } + + constructor(private service: GrpcAuthService, + private toast: ToastService, + private dialog: MatDialog) { } public ngOnInit(): void { - this.getOTP(); + this.getMFAs(); } public ngOnDestroy(): void { @@ -50,7 +65,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { dialogRef.afterClosed().subscribe((code) => { if (code) { this.service.VerifyMfaOTP(code).then(() => { - this.getOTP(); + this.getMFAs(); }); } }); @@ -59,7 +74,44 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { }); } - public getOTP(): void { + public addU2F(): void { + this.service.AddMyMfaU2F().then((u2fresp) => { + const webauthn: WebAuthNResponse.AsObject = u2fresp.toObject(); + const credOptions: CredentialCreationOptions = JSON.parse(atob(webauthn.publicKey as string)); + + if (credOptions.publicKey?.challenge) { + credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any); + credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any); + if (credOptions.publicKey.excludeCredentials) { + credOptions.publicKey.excludeCredentials.map(cred => { + cred.id = _base64ToArrayBuffer(cred.id as any); + return cred; + }); + } + console.log(credOptions); + const dialogRef = this.dialog.open(DialogU2FComponent, { + width: '400px', + data: { + credOptions, + type: U2FComponentDestination.MFA, + }, + }); + + dialogRef.afterClosed().subscribe(done => { + if (done) { + this.getMFAs(); + } else { + this.getMFAs(); + } + }); + } + + }, error => { + this.toast.showError(error); + }); + } + + public getMFAs(): void { this.service.GetMyMfas().then(mfas => { this.dataSource = new MatTableDataSource(mfas.toObject().mfasList); this.dataSource.sort = this.sort; @@ -73,13 +125,13 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { }); } - public deleteMFA(type: MfaType): void { + public deleteMFA(type: MfaType, id?: string): void { const dialogRef = this.dialog.open(WarnDialogComponent, { data: { confirmKey: 'ACTIONS.DELETE', cancelKey: 'ACTIONS.CANCEL', - titleKey: 'USER.MFA.DIALOG.OTP_DELETE_TITLE', - descriptionKey: 'USER.MFA.DIALOG.OTP_DELETE_DESCRIPTION', + titleKey: 'USER.MFA.DIALOG.MFA_DELETE_TITLE', + descriptionKey: 'USER.MFA.DIALOG.MFA_DELETE_DESCRIPTION', }, width: '400px', }); @@ -94,7 +146,19 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { if (index > -1) { this.dataSource.data.splice(index, 1); } - this.getOTP(); + this.getMFAs(); + }).catch(error => { + this.toast.showError(error); + }); + } else if (type === MfaType.MFATYPE_U2F && id) { + this.service.RemoveMyMfaU2F(id).then(() => { + this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + + const index = this.dataSource.data.findIndex(mfa => mfa.type === type); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); }).catch(error => { this.toast.showError(error); }); @@ -102,4 +166,6 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { } }); } + + } diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html index 9a4eb065e1..0d5aa2426b 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-otp/dialog-otp.component.html @@ -1,6 +1,6 @@

{{'USER.MFA.OTP_DIALOG_TITLE' | translate}}

-

{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}

+

{{'USER.MFA.OTP_DIALOG_DESCRIPTION' | translate}}

@@ -15,4 +15,4 @@ -
\ No newline at end of file +
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html new file mode 100644 index 0000000000..fa9a136f1e --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html @@ -0,0 +1,19 @@ +

{{'USER.MFA.U2F_DIALOG_TITLE' | translate}}

+
+

{{'USER.MFA.U2F_DIALOG_DESCRIPTION' | translate}}

+ + + {{'USER.MFA.U2F_NAME' | translate}} + + + + + +

{{error}}

+
+
+ + +
\ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.scss b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.scss new file mode 100644 index 0000000000..94688b0fb8 --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.scss @@ -0,0 +1,27 @@ +.qrcode-wrapper { + display: flex; + align-content: center; + + .qrcode { + margin: 1rem auto; + } +} + +.form-field { + width: 100%; +} + +.error { + color: #f44336; + font-size: 14px; +} + +.action { + display: flex; + justify-content: flex-end; + + button { + margin-left: .5rem; + border-radius: .5rem; + } +} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.ts new file mode 100644 index 0000000000..cee3116522 --- /dev/null +++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.ts @@ -0,0 +1,110 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; +import { take } from 'rxjs/operators'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { ToastService } from 'src/app/services/toast.service'; + +export function _arrayBufferToBase64(buffer: any): string { + let binary = ''; + const bytes = new Uint8Array(buffer); + const len = bytes.byteLength; + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary).replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); +} + +export enum U2FComponentDestination { + MFA = 'mfa', + PASSWORDLESS = 'passwordless', +} + +@Component({ + selector: 'app-dialog-u2f', + templateUrl: './dialog-u2f.component.html', + styleUrls: ['./dialog-u2f.component.scss'], +}) +export class DialogU2FComponent { + private type!: U2FComponentDestination; + public name: string = ''; + public error: string = ''; + public loading: boolean = false; + + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: { credOptions: any; type: U2FComponentDestination; }, + private service: GrpcAuthService, private translate: TranslateService, private toast: ToastService) { + this.type = data.type; + } + + public closeDialog(): void { + this.dialogRef.close(); + } + + public closeDialogWithCode(): void { + this.error = ''; + this.loading = true; + if (this.name && this.data.credOptions.publicKey) { + // this.data.credOptions.publicKey.rp.id = 'localhost'; + navigator.credentials.create(this.data.credOptions).then((resp) => { + if (resp && + (resp as any).response.attestationObject && + (resp as any).response.clientDataJSON && + (resp as any).rawId) { + + const attestationObject = (resp as any).response.attestationObject; + const clientDataJSON = (resp as any).response.clientDataJSON; + const rawId = (resp as any).rawId; + + const data = JSON.stringify({ + id: resp.id, + rawId: _arrayBufferToBase64(rawId), + type: resp.type, + response: { + attestationObject: _arrayBufferToBase64(attestationObject), + clientDataJSON: _arrayBufferToBase64(clientDataJSON), + }, + }); + + const base64 = btoa(data); + if (this.type === U2FComponentDestination.MFA) { + this.service.VerifyMyMfaU2F(base64, this.name).then(() => { + this.translate.get('USER.MFA.U2F_SUCCESS').pipe(take(1)).subscribe(msg => { + this.toast.showInfo(msg); + }); + this.dialogRef.close(true); + this.loading = false; + }).catch(error => { + this.loading = false; + this.toast.showError(error); + }); + } else if (this.type === U2FComponentDestination.PASSWORDLESS) { + this.service.verifyMyPasswordless(base64, this.name).then(() => { + this.translate.get('USER.PASSWORDLESS.U2F_SUCCESS').pipe(take(1)).subscribe(msg => { + this.toast.showInfo(msg); + }); + this.dialogRef.close(true); + this.loading = false; + }).catch(error => { + this.loading = false; + this.toast.showError(error); + }); + } + } else { + this.loading = false; + this.translate.get('USER.MFA.U2F_ERROR').pipe(take(1)).subscribe(msg => { + this.toast.showInfo(msg); + }); + this.dialogRef.close(true); + } + }).catch(error => { + this.loading = false; + this.error = error; + this.toast.showInfo(error.message); + }); + } + + } +} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html index 37056e74fe..81ded0f7ef 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html @@ -5,7 +5,7 @@
{{data.labelKey | translate }} - +
diff --git a/console/src/app/pages/users/user-detail/u2f-util.ts b/console/src/app/pages/users/user-detail/u2f-util.ts new file mode 100644 index 0000000000..02e61dfa5c --- /dev/null +++ b/console/src/app/pages/users/user-detail/u2f-util.ts @@ -0,0 +1,10 @@ + +export function _base64ToArrayBuffer(base64: string): any { + const binaryString = atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} diff --git a/console/src/app/pages/users/user-detail/user-detail.module.ts b/console/src/app/pages/users/user-detail/user-detail.module.ts index a217c0e7b8..cab3739fbb 100644 --- a/console/src/app/pages/users/user-detail/user-detail.module.ts +++ b/console/src/app/pages/users/user-detail/user-detail.module.ts @@ -29,10 +29,12 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod 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 { AuthPasswordlessComponent } from './auth-user-detail/auth-passwordless/auth-passwordless.component'; import { AuthUserDetailComponent } from './auth-user-detail/auth-user-detail.component'; import { AuthUserMfaComponent } from './auth-user-detail/auth-user-mfa/auth-user-mfa.component'; import { CodeDialogComponent } from './auth-user-detail/code-dialog/code-dialog.component'; import { DialogOtpComponent } from './auth-user-detail/dialog-otp/dialog-otp.component'; +import { DialogU2FComponent } from './auth-user-detail/dialog-u2f/dialog-u2f.component'; import { EditDialogComponent } from './auth-user-detail/edit-dialog/edit-dialog.component'; import { ResendEmailDialogComponent } from './auth-user-detail/resend-email-dialog/resend-email-dialog.component'; import { ThemeSettingComponent } from './auth-user-detail/theme-setting/theme-setting.component'; @@ -46,6 +48,7 @@ import { ShowKeyDialogModule } from './machine-keys/show-key-dialog/show-key-dia import { MembershipsComponent } from './memberships/memberships.component'; import { PasswordComponent } from './password/password.component'; import { UserDetailRoutingModule } from './user-detail-routing.module'; +import { PasswordlessComponent } from './user-detail/passwordless/passwordless.component'; import { UserDetailComponent } from './user-detail/user-detail.component'; import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component'; @@ -56,7 +59,9 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component'; DialogOtpComponent, EditDialogComponent, AuthUserMfaComponent, + AuthPasswordlessComponent, UserMfaComponent, + PasswordlessComponent, ThemeSettingComponent, PasswordComponent, CodeDialogComponent, @@ -65,6 +70,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component'; ExternalIdpsComponent, ContactComponent, ResendEmailDialogComponent, + DialogU2FComponent, ], imports: [ UserDetailRoutingModule, diff --git a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html new file mode 100644 index 0000000000..6f6f40f267 --- /dev/null +++ b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html @@ -0,0 +1,43 @@ + + +
{{ 'USER.MFA.TABLETYPE' | translate }} {{'USER.MFA.TYPE.'+ mfa.type | translate}} {{ 'USER.MFA.ATTRIBUTE' | translate }} + {{ mfa?.attribute }} + + {{ 'USER.MFA.TABLESTATE' | translate }} @@ -20,7 +28,7 @@ {{ 'USER.MFA.TABLEACTIONS' | translate }}
+ + + + + + + + + + + + + + + + + +
{{ 'USER.PASSWORDLESS.NAME' | translate }} + {{ mfa?.name }} + + {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} + {{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}} + + + {{ 'USER.PASSWORDLESS.TABLEACTIONS' | translate }} + +
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.scss b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.scss new file mode 100644 index 0000000000..0c317899c6 --- /dev/null +++ b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.scss @@ -0,0 +1,26 @@ + +.centered { + display: flex; + align-items: center; + + i { + margin-left: 1rem; + color: var(--color-main); + } +} + +.table { + width: 100%; + + td, + th { + &:first-child { + padding-left: 0; + padding-right: 1rem; + } + + &:last-child { + padding-right: 0; + } + } +} diff --git a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.spec.ts b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.spec.ts new file mode 100644 index 0000000000..fbee4d4c7f --- /dev/null +++ b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { AuthPasswordlessComponent } from './auth-passwordless.component'; + +describe('AuthPasswordlessComponent', () => { + let component: AuthPasswordlessComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AuthPasswordlessComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AuthPasswordlessComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.ts b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.ts new file mode 100644 index 0000000000..2312bff049 --- /dev/null +++ b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.ts @@ -0,0 +1,84 @@ +import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSort } from '@angular/material/sort'; +import { MatTable, MatTableDataSource } from '@angular/material/table'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; +import { MFAState, WebAuthNToken } from 'src/app/proto/generated/auth_pb'; +import { UserView } from 'src/app/proto/generated/management_pb'; +import { ManagementService } from 'src/app/services/mgmt.service'; +import { ToastService } from 'src/app/services/toast.service'; + +export interface WebAuthNOptions { + challenge: string; + rp: { name: string, id: string; }; + user: { name: string, id: string, displayName: string; }; + pubKeyCredParams: any; + authenticatorSelection: { userVerification: string; }; + timeout: number; + attestation: string; +} + +@Component({ + selector: 'app-passwordless', + templateUrl: './passwordless.component.html', + styleUrls: ['./passwordless.component.scss'], +}) +export class PasswordlessComponent implements OnInit, OnDestroy { + @Input() private user!: UserView.AsObject; + public displayedColumns: string[] = ['name', 'state', 'actions']; + private loadingSubject: BehaviorSubject = new BehaviorSubject(false); + public loading$: Observable = this.loadingSubject.asObservable(); + + @ViewChild(MatTable) public table!: MatTable; + @ViewChild(MatSort) public sort!: MatSort; + public dataSource!: MatTableDataSource; + + public MFAState: any = MFAState; + public error: string = ''; + + constructor(private service: ManagementService, + private toast: ToastService, + private dialog: MatDialog) { } + + public ngOnInit(): void { + this.getPasswordless(); + } + + public ngOnDestroy(): void { + this.loadingSubject.complete(); + } + + public getPasswordless(): void { + this.service.GetPasswordless(this.user.id).then(passwordless => { + console.log(passwordless.toObject().tokensList); + this.dataSource = new MatTableDataSource(passwordless.toObject().tokensList); + this.dataSource.sort = this.sort; + }).catch(error => { + this.error = error.message; + }); + } + + public deletePasswordless(id?: string): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'USER.PASSWORDLESS.DIALOG.DELETE_TITLE', + descriptionKey: 'USER.PASSWORDLESS.DIALOG.DELETE_DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp && id) { + this.service.RemovePasswordless(id, this.user.id).then(() => { + this.toast.showInfo('USER.TOAST.PASSWORDLESSREMOVED', true); + this.getPasswordless(); + }).catch(error => { + this.toast.showError(error); + }); + } + }); + } +} 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 b430a4d878..61b915c0d3 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 @@ -84,6 +84,8 @@ + + - + + + + + + diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts index e91c170d77..e06e324a21 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts @@ -20,7 +20,7 @@ export interface MFAItem { styleUrls: ['./user-mfa.component.scss'], }) export class UserMfaComponent implements OnInit, OnDestroy { - public displayedColumns: string[] = ['type', 'state', 'actions']; + public displayedColumns: string[] = ['type', 'attr', 'state', 'actions']; @Input() private user!: UserView.AsObject; public mfaSubject: BehaviorSubject = new BehaviorSubject([]); private loadingSubject: BehaviorSubject = new BehaviorSubject(false); @@ -37,7 +37,7 @@ export class UserMfaComponent implements OnInit, OnDestroy { constructor(private mgmtUserService: ManagementService, private dialog: MatDialog, private toast: ToastService) { } public ngOnInit(): void { - this.getOTP(); + this.getMFAs(); } public ngOnDestroy(): void { @@ -45,8 +45,9 @@ export class UserMfaComponent implements OnInit, OnDestroy { this.loadingSubject.complete(); } - public getOTP(): void { + public getMFAs(): void { this.mgmtUserService.getUserMfas(this.user.id).then(mfas => { + console.log(mfas.toObject().mfasList); this.dataSource = new MatTableDataSource(mfas.toObject().mfasList); this.dataSource.sort = this.sort; }).catch(error => { @@ -54,13 +55,14 @@ export class UserMfaComponent implements OnInit, OnDestroy { }); } - public deleteMFA(type: MfaType): void { + public deleteMFA(type: MfaType, id?: string): void { + console.log(type, id); const dialogRef = this.dialog.open(WarnDialogComponent, { data: { confirmKey: 'ACTIONS.DELETE', cancelKey: 'ACTIONS.CANCEL', - titleKey: 'USER.MFA.DIALOG.OTP_DELETE_TITLE', - descriptionKey: 'USER.MFA.DIALOG.OTP_DELETE_DESCRIPTION', + titleKey: 'USER.MFA.DIALOG.MFA_DELETE_TITLE', + descriptionKey: 'USER.MFA.DIALOG.MFA_DELETE_DESCRIPTION', }, width: '400px', }); @@ -75,7 +77,20 @@ export class UserMfaComponent implements OnInit, OnDestroy { if (index > -1) { this.dataSource.data.splice(index, 1); } - this.getOTP(); + this.getMFAs(); + }).catch(error => { + this.toast.showError(error); + }); + } else if (type === MfaType.MFATYPE_U2F && id) { + console.log(this.user.id, id); + this.mgmtUserService.RemoveMfaU2F(this.user.id, id).then(() => { + this.toast.showInfo('USER.TOAST.U2FREMOVED', true); + + const index = this.dataSource.data.findIndex(mfa => mfa.type === type); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getMFAs(); }).catch(error => { this.toast.showError(error); }); diff --git a/console/src/app/pages/users/user-list/user-table/user-table.component.html b/console/src/app/pages/users/user-list/user-table/user-table.component.html index a508c5d858..74d0cad53e 100644 --- a/console/src/app/pages/users/user-list/user-table/user-table.component.html +++ b/console/src/app/pages/users/user-list/user-table/user-table.component.html @@ -1,4 +1,4 @@ - diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index adad72a275..3e4f5cc167 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -11,6 +11,7 @@ import { DefaultLabelPolicyUpdate, DefaultLabelPolicyView, DefaultLoginPolicy, + DefaultLoginPolicyRequest, DefaultLoginPolicyView, DefaultPasswordAgePolicyRequest, DefaultPasswordAgePolicyView, @@ -36,6 +37,8 @@ import { IdpSearchRequest, IdpSearchResponse, IdpView, + MultiFactor, + MultiFactorsResult, OidcIdpConfig, OidcIdpConfigCreate, OidcIdpConfigUpdate, @@ -46,6 +49,8 @@ import { OrgSetUpRequest, OrgSetUpResponse, RemoveIamMemberRequest, + SecondFactor, + SecondFactorsResult, ViewID, Views, } from '../proto/generated/admin_pb'; @@ -73,6 +78,32 @@ export class AdminService { return this.grpcService.admin.setUpOrg(req); } + public getDefaultLoginPolicyMultiFactors(): Promise { + const req = new Empty(); + return this.grpcService.admin.getDefaultLoginPolicyMultiFactors(req); + } + + public addMultiFactorToDefaultLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.admin.addMultiFactorToDefaultLoginPolicy(req); + } + + public RemoveMultiFactorFromDefaultLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.admin.removeMultiFactorFromDefaultLoginPolicy(req); + } + + public GetDefaultLoginPolicySecondFactors(): Promise { + const req = new Empty(); + return this.grpcService.admin.getDefaultLoginPolicySecondFactors(req); + } + + public AddSecondFactorToDefaultLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.admin.addSecondFactorToDefaultLoginPolicy(req); + } + + public RemoveSecondFactorFromDefaultLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.admin.removeSecondFactorFromDefaultLoginPolicy(req); + } + public GetIamMemberRoles(): Promise { const req = new Empty(); return this.grpcService.admin.getIamMemberRoles(req); @@ -184,7 +215,7 @@ export class AdminService { return this.grpcService.admin.getDefaultLoginPolicy(req); } - public UpdateDefaultLoginPolicy(req: DefaultLoginPolicy): Promise { + public UpdateDefaultLoginPolicy(req: DefaultLoginPolicyRequest): Promise { return this.grpcService.admin.updateDefaultLoginPolicy(req); } diff --git a/console/src/app/services/grpc-auth.service.ts b/console/src/app/services/grpc-auth.service.ts index 67d5f4def4..0022c24126 100644 --- a/console/src/app/services/grpc-auth.service.ts +++ b/console/src/app/services/grpc-auth.service.ts @@ -33,6 +33,10 @@ import { UserView, VerifyMfaOtp, VerifyUserPhoneRequest, + VerifyWebAuthN, + WebAuthNResponse, + WebAuthNTokenID, + WebAuthNTokens, } from '../proto/generated/auth_pb'; import { GrpcService } from './grpc.service'; import { StorageKey, StorageService } from './storage.service'; @@ -328,6 +332,56 @@ export class GrpcAuthService { ); } + public AddMyMfaU2F(): Promise { + return this.grpcService.auth.addMyMfaU2F( + new Empty(), + ); + } + + public RemoveMyMfaU2F(id: string): Promise { + const req = new WebAuthNTokenID(); + req.setId(id); + return this.grpcService.auth.removeMyMfaU2F(req); + } + + public VerifyMyMfaU2F(credential: string, tokenname: string): Promise { + const req = new VerifyWebAuthN(); + req.setPublicKeyCredential(credential); + req.setTokenName(tokenname); + + return this.grpcService.auth.verifyMyMfaU2F( + req, + ); + } + + public GetMyPasswordless(): Promise { + return this.grpcService.auth.getMyPasswordless( + new Empty(), + ); + } + + public AddMyPasswordless(): Promise { + return this.grpcService.auth.addMyPasswordless( + new Empty(), + ); + } + + public RemoveMyPasswordless(id: string): Promise { + const req = new WebAuthNTokenID(); + req.setId(id); + return this.grpcService.auth.removeMyPasswordless(req); + } + + public verifyMyPasswordless(credential: string, tokenname: string): Promise { + const req = new VerifyWebAuthN(); + req.setPublicKeyCredential(credential); + req.setTokenName(tokenname); + + return this.grpcService.auth.verifyMyPasswordless( + req, + ); + } + public RemoveMfaOTP(): Promise { return this.grpcService.auth.removeMfaOTP( new Empty(), diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index 31fef046a0..f76b8f5cc8 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -3,6 +3,7 @@ import { Empty } from 'google-protobuf/google/protobuf/empty_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { BehaviorSubject } from 'rxjs'; +import { MultiFactorsResult } from '../proto/generated/admin_pb'; import { AddMachineKeyRequest, AddMachineKeyResponse, @@ -50,6 +51,7 @@ import { MachineKeySearchResponse, MachineKeyType, MachineResponse, + MultiFactor, NotificationType, OIDCApplicationCreate, OIDCConfig, @@ -122,6 +124,8 @@ import { ProjectView, RemoveOrgDomainRequest, RemoveOrgMemberRequest, + SecondFactor, + SecondFactorsResult, SetPasswordNotificationRequest, UpdateMachineRequest, UpdateUserAddressRequest, @@ -152,6 +156,8 @@ import { UserSearchResponse, UserView, ValidateOrgDomainRequest, + WebAuthNTokenID, + WebAuthNTokens, ZitadelDocs, } from '../proto/generated/management_pb'; import { GrpcService } from './grpc.service'; @@ -185,6 +191,45 @@ export class ManagementService { return this.grpcService.mgmt.searchIdps(req); } + public GetPasswordless(userId: string): Promise { + const req = new UserID(); + req.setId(userId); + return this.grpcService.mgmt.getPasswordless(req); + } + + public RemovePasswordless(id: string, userId: string): Promise { + const req = new WebAuthNTokenID(); + req.setId(id); + req.setUserId(userId); + return this.grpcService.mgmt.removePasswordless(req); + } + + public GetLoginPolicyMultiFactors(): Promise { + const req = new Empty(); + return this.grpcService.mgmt.getLoginPolicyMultiFactors(req); + } + + public AddMultiFactorToLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.mgmt.addMultiFactorToLoginPolicy(req); + } + + public RemoveMultiFactorFromLoginPolicy(req: MultiFactor): Promise { + return this.grpcService.mgmt.removeMultiFactorFromLoginPolicy(req); + } + + public GetLoginPolicySecondFactors(): Promise { + const req = new Empty(); + return this.grpcService.mgmt.getLoginPolicySecondFactors(req); + } + + public AddSecondFactorToLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.mgmt.addSecondFactorToLoginPolicy(req); + } + + public RemoveSecondFactorFromLoginPolicy(req: SecondFactor): Promise { + return this.grpcService.mgmt.removeSecondFactorFromLoginPolicy(req); + } + public GetLoginPolicy(): Promise { const req = new Empty(); return this.grpcService.mgmt.getLoginPolicy(req); @@ -683,6 +728,13 @@ export class ManagementService { return this.grpcService.mgmt.removeMfaOTP(req); } + public RemoveMfaU2F(userid: string, id: string): Promise { + const req = new WebAuthNTokenID(); + req.setId(id); + req.setUserId(userid); + return this.grpcService.mgmt.removeMfaU2F(req); + } + public SaveUserProfile( id: string, firstName?: string, @@ -1307,21 +1359,7 @@ export class ManagementService { return this.grpcService.mgmt.updateApplication(req); } - public UpdateOIDCAppConfig(projectId: string, - appId: string, oidcConfig: OIDCConfig.AsObject): Promise { - const req = new OIDCConfigUpdate(); - req.setProjectId(projectId); - req.setApplicationId(appId); - req.setRedirectUrisList(oidcConfig.redirectUrisList); - req.setResponseTypesList(oidcConfig.responseTypesList); - req.setAuthMethodType(oidcConfig.authMethodType); - req.setPostLogoutRedirectUrisList(oidcConfig.postLogoutRedirectUrisList); - req.setGrantTypesList(oidcConfig.grantTypesList); - req.setApplicationType(oidcConfig.applicationType); - req.setDevMode(oidcConfig.devMode); - req.setAccessTokenType(oidcConfig.accessTokenType); - req.setAccessTokenRoleAssertion(oidcConfig.accessTokenRoleAssertion); - req.setIdTokenRoleAssertion(oidcConfig.idTokenRoleAssertion); + public UpdateOIDCAppConfig(req: OIDCConfigUpdate): Promise { return this.grpcService.mgmt.updateApplicationOIDCConfig(req); } } diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index a003ec328b..7f12d7b93f 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -152,16 +152,20 @@ }, "EMPTY":"Keine Einträge" }, - "MFA": { + "PASSWORDLESS": { "TABLETYPE":"Typ", "TABLESTATE":"Status", + "NAME":"Name", "TABLEACTIONS":"Aktionen", - "TITLE": "Multifaktor-Authentisierung", - "DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um Dein Konto optimal zu schützen.", + "TITLE": "Passwortlose Authentifizierungsmethoden", + "DESCRIPTION": "Füge WebAuthn kompatible Authentifikatoren hinzu um dich passwortlos anzumelden.", "MANAGE_DESCRIPTION": "Verwalte die Multifaktor-Merkmale Deiner Benutzer.", - "OTP": "OTP konfigurieren", - "OTP_DIALOG_TITLE": "OTP hinzufügen", - "OTP_DIALOG_DESCRIPTION": "Scanne den QR-Code mit einer Authenticator App und verifiziere den erhaltenen Code, um OTP zu aktivieren.", + "U2F":"Authentifikator hinzufügen", + "U2F_DIALOG_TITLE": "Authentifikator hinzufügen", + "U2F_DIALOG_DESCRIPTION": "Gib einen Namen für den von dir verwendeten Login an.", + "U2F_SUCCESS":"Passwordless erfolgreich erstellt!", + "U2F_ERROR":"Ein Fehler ist aufgetreten!", + "U2F_NAME":"Authentifikator Name", "TYPE": { "0":"Keine MFA definiert", "1":"OTP", @@ -174,8 +178,41 @@ "3": "Gelöscht" }, "DIALOG": { - "OTP_DELETE_TITLE":"OTP Faktor entfernen", - "OTP_DELETE_DESCRIPTION":"Sie sind im Begriff OTP als Zweitfaktormethode zu entfernen. Sind sie sicher?" + "DELETE_TITLE":"Passwordless entfernen", + "DELETE_DESCRIPTION":"Sie sind im Begriff eine Passwortlose Authentifizierungsmethode zu entfernen. Sind sie sicher?" + } + }, + "MFA": { + "TABLETYPE":"Typ", + "TABLESTATE":"Status", + "ATTRIBUTE":"Attribut", + "TABLEACTIONS":"Aktionen", + "TITLE": "Multifaktor-Authentisierung", + "DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um Dein Konto optimal zu schützen.", + "MANAGE_DESCRIPTION": "Verwalte die Multifaktor-Merkmale Deiner Benutzer.", + "OTP": "OTP konfigurieren", + "OTP_DIALOG_TITLE": "OTP hinzufügen", + "OTP_DIALOG_DESCRIPTION": "Scanne den QR-Code mit einer Authenticator App und verifiziere den erhaltenen Code, um OTP zu aktivieren.", + "U2F":"U2F hinzufügen", + "U2F_DIALOG_TITLE": "U2F hinzufügen", + "U2F_DIALOG_DESCRIPTION": "Gib einen Namen für den von dir verwendeten Universellen Multifaktor an.", + "U2F_SUCCESS":"U2F erfolgreich erstellt!", + "U2F_ERROR":"Ein Fehler ist aufgetreten!", + "U2F_NAME":"U2F Name", + "TYPE": { + "0":"Keine MFA definiert", + "1":"OTP", + "2":"U2F" + }, + "STATE": { + "0": "Kein Status", + "1": "Nicht bereit", + "2": "Bereit", + "3": "Gelöscht" + }, + "DIALOG": { + "MFA_DELETE_TITLE":"Zweiten Faktor entfernen", + "MFA_DELETE_DESCRIPTION":"Sie sind im Begriff eine Zweitfaktormethode zu entfernen. Sind sie sicher?" } }, "EXTERNALIDP": { @@ -345,6 +382,8 @@ "PHONEVERIFICATIONSENT":"Bestätigungscode per Telefonnummer gesendet.", "EMAILVERIFICATIONSENT":"Bestätigungscode per E-Mail gesendet.", "OTPREMOVED":"OTP entfernt.", + "U2FREMOVED":"U2F entfernt.", + "PASSWORDLESSREMOVED":"Passwordless entfernt.", "INITIALPASSWORDSET":"Initiales Passwort gesetzt.", "PASSWORDNOTIFICATIONSENT":"Passwortänderung mittgeteilt.", "PASSWORDCHANGED":"Passwort geändert.", @@ -551,7 +590,9 @@ "ALLOWREGISTER":"Registrieren erlaubt", "ALLOWUSERNAMEPASSWORD_DESC":"Der konventionelle Login mit Benutzername und Passwort wird erlaubt.", "ALLOWEXTERNALIDP_DESC":"Der Login wird für die darunter liegenden Identity Provider erlaubt.", - "ALLOWREGISTER_DESC":"Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers." + "ALLOWREGISTER_DESC":"Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.", + "FORCEMFA":"Mfa erzwingen", + "FORCEMFA_DESC":"Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden." }, "RESET":"Richtlinie zurücksetzen", "CREATECUSTOM":"Benutzerdefinierte Richtlinie erstellen", @@ -763,7 +804,7 @@ }, "IDP":{ "LIST": { - "TITLE":"Identitäts Providers", + "TITLE":"Identitäts Provider", "DESCRIPTION":"Definieren Sie hier Ihre zusätzlichen Idps, die sie für die Authentifizierung in Ihren Organisationen verwenden können." }, "CREATE": { @@ -826,6 +867,37 @@ "DELETED":"Idp erfolgreich gelöscht!" } }, + "MFA":{ + "LIST": { + "MULTIFACTORTITLE":"Multifaktoren", + "MULTIFACTORDESCRIPTION":"Definieren Sie hier Ihre Multifaktoren, die sie für die Authentifizierung verwenden können.", + "SECONDFACTORTITLE":"Zweitfaktoren", + "SECONDFACTORDESCRIPTION":"Definieren Sie hier Ihre Zweitfaktoren, die sie für die Authentifizierung verwenden können." + }, + "CREATE": { + "TITLE":"Neuer Faktor", + "DESCRIPTION":"Definieren Sie hier den gewünschten Typ." + }, + "DELETE": { + "TITLE":"Faktor löschen", + "DESCRIPTION":"Sie sind im Begriff einen Faktor zu löschen. Die daruch hervorgerufenen Änderungen sind unwiederruflich. Wollen Sie dies wirklich tun?" + }, + "TOAST": { + "ADDED":"Erfolgreich hinzugefügt.", + "SAVED": "Erfolgreich gespeichert.", + "DELETED":"Mfa erfolgreich gelöscht!" + }, + "TYPE":"Typ", + "MULTIFACTORTYPES": { + "0":"Unknown", + "1":"U2F with Pin" + }, + "SECONDFACTORTYPES": { + "0":"Unknown", + "1":"OTP", + "2":"U2F" + } + }, "LOGINPOLICY": { "CREATE": { "TITLE":"Login Policy", @@ -836,6 +908,11 @@ "TITLE":"Identity Provider hinzufügen", "DESCRIPTION":"Sie können vordefinierte oder selbsterstellten Provider auswählen", "SELECTIDPS":"Identity Provider" + }, + "PASSWORDLESS":"Passwordloser Login", + "PASSWORDLESSTYPE": { + "0":"Nicht erlaubt", + "1":"Erlaubt" } }, "APP": { @@ -910,8 +987,11 @@ "OVERVIEWTITLE":"Deine Konfiguration ist bereit. Du kannst sie hier nochmals prüfen.", "ACCESSTOKENROLEASSERTION":"Benutzerrollen dem Access Token hinzufügen", "ACCESSTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Access Token die Rollen des Authentifizierten Benutzers hinzugefügt.", - "IDTOKENROLEASSERTION":"Benutzerrollen dem Id Token hinzufügen", - "IDTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Id Token die Rollen des Authentifizierten Benutzers hinzugefügt." + "IDTOKENROLEASSERTION":"Benutzerrollen im ID Token", + "IDTOKENROLEASSERTION_DESCRIPTION":"Bei Auswahl werden dem Id Token die Rollen des Authentifizierten Benutzers hinzugefügt.", + "IDTOKENUSERINFOASSERTION":"User Info im ID Token", + "IDTOKENUSERINFOASSERTION_DESCRIPTION":"Ermöglich OIDC clients claims von profile, email, phone und address direkt vom ID Token zu beziehen.", + "CLOCKSKEW":"ermöglicht Clients, den Taktversatz von OP und Client zu verarbeiten. Die Dauer (0-5s) wird der exp addiert und von iats, auth_time und nbf abgezogen." }, "TOAST": { "REACTIVATED":"Anwendung reaktiviert.", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index b4f01ba663..ee90a3b6c8 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -152,16 +152,20 @@ }, "EMPTY":"No entries" }, - "MFA": { + "PASSWORDLESS": { "TABLETYPE":"Type", "TABLESTATE":"Status", + "NAME":"Name", "TABLEACTIONS":"Actions", - "TITLE": "Multifactor Authentication", - "DESCRIPTION": "Add a second factor to ensure optimal security for your account.", + "TITLE": "Passwordless Authentication", + "DESCRIPTION": "Add WebAuthn based Authentication Methods to log onto ZITADEL passwordless.", "MANAGE_DESCRIPTION": "Manage the second factor methods of your users.", - "OTP": "Configure OTP", - "OTP_DIALOG_TITLE": "Add OTP", - "OTP_DIALOG_DESCRIPTION": "Scan the QR code with an authenticator app and enter the code below to verify and activate the OTP method.", + "U2F":"Add authenticator", + "U2F_DIALOG_TITLE": "Verify authenticator", + "U2F_DIALOG_DESCRIPTION": "Enter a name for your used passwordless Login", + "U2F_SUCCESS":"Passwordless Auth created successfully!", + "U2F_ERROR":"An error during U2F setup occurred!", + "U2F_NAME":"Authenticator Name", "TYPE": { "0": "No MFA defined", "1": "OTP", @@ -174,8 +178,41 @@ "3": "Deleted" }, "DIALOG": { - "OTP_DELETE_TITLE":"Remove OTP Factor", - "OTP_DELETE_DESCRIPTION":"You are about to delete OTP as second factor. Are you sure?" + "DELETE_TITLE":"Remove Passwordless Authentication Method", + "DELETE_DESCRIPTION":"You are about to delete a passwordless Authentication method. Are you sure?" + } + }, + "MFA": { + "TABLETYPE":"Type", + "TABLESTATE":"Status", + "ATTRIBUTE":"Attribut", + "TABLEACTIONS":"Actions", + "TITLE": "Multifactor Authentication", + "DESCRIPTION": "Add a second factor to ensure optimal security for your account.", + "MANAGE_DESCRIPTION": "Manage the second factor methods of your users.", + "OTP": "Configure OTP", + "OTP_DIALOG_TITLE": "Add OTP", + "OTP_DIALOG_DESCRIPTION": "Scan the QR code with an authenticator app and enter the code below to verify and activate the OTP method.", + "U2F":"Add U2F", + "U2F_DIALOG_TITLE": "Verify U2F", + "U2F_DIALOG_DESCRIPTION": "Enter a name for your used universal Multifactor.", + "U2F_SUCCESS":"U2F created successfully!", + "U2F_ERROR":"An error during U2F setup occurred!", + "U2F_NAME":"U2F Name", + "TYPE": { + "0": "No MFA defined", + "1": "OTP", + "2": "U2F" + }, + "STATE": { + "0": "No State", + "1": "Not Ready", + "2": "Ready", + "3": "Deleted" + }, + "DIALOG": { + "MFA_DELETE_TITLE":"Remove Secondfactor", + "MFA_DELETE_DESCRIPTION":"You are about to delete a second factor. Are you sure?" } }, "EXTERNALIDP": { @@ -345,6 +382,8 @@ "PHONEVERIFICATIONSENT":"Phone verification code sent.", "EMAILVERIFICATIONSENT":"E-mail verification code sent.", "OTPREMOVED":"OTP removed.", + "U2FREMOVED":"U2F removed.", + "PASSWORDLESSREMOVED":"Passwordless removed.", "INITIALPASSWORDSET":"Initial password set.", "PASSWORDNOTIFICATIONSENT":"Password change notification sent.", "PASSWORDCHANGED":"Password changed successfully.", @@ -551,7 +590,9 @@ "ALLOWREGISTER":"Register allowed", "ALLOWUSERNAMEPASSWORD_DESC":"The conventional login with user name and password is allowed.", "ALLOWEXTERNALIDP_DESC":"The login is allowed for the underlying identity providers", - "ALLOWREGISTER_DESC":"If the option is selected, an additional step for registering a user appears in the login." + "ALLOWREGISTER_DESC":"If the option is selected, an additional step for registering a user appears in the login.", + "FORCEMFA":"Force MFA", + "FORCEMFA_DESC":"If the option is selected, users have to configure a second factor for login." }, "RESET":"Reset Policy", "CREATECUSTOM":"Create Custom Policy", @@ -826,6 +867,37 @@ "DELETED":"Idp removed successfully!" } }, + "MFA":{ + "LIST": { + "MULTIFACTORTITLE":"Multifactors", + "MULTIFACTORDESCRIPTION":"Define your Multifactors for Authentication here.", + "SECONDFACTORTITLE":"Secondfactors", + "SECONDFACTORDESCRIPTION":"Define your Secondfactors for Authentication here." + }, + "CREATE": { + "TITLE":"New Factor", + "DESCRIPTION":"Select your new Factor type." + }, + "DELETE": { + "TITLE":"Delete Factor", + "DESCRIPTION":"You are about to delete a Factor from Login Policy. Are you sure?" + }, + "TOAST": { + "ADDED":"Added successfully.", + "SAVED": "Saved successfully.", + "DELETED":"Removed successfully" + }, + "TYPE":"Type", + "MULTIFACTORTYPES": { + "0":"Unknown", + "1":"U2F with Pin" + }, + "SECONDFACTORTYPES": { + "0":"Unknown", + "1":"OTP", + "2":"U2F" + } + }, "LOGINPOLICY": { "CREATE": { "TITLE":"Login Policy", @@ -836,6 +908,11 @@ "TITLE":"Add Identity Provider", "DESCRIPTION":"You can select predefined or selfcreated providers for authentication.", "SELECTIDPS":"Identity providers" + }, + "PASSWORDLESS":"Passwordless Login", + "PASSWORDLESSTYPE": { + "0":"Not allowed", + "1":"Allowed" } }, "APP": { @@ -910,8 +987,11 @@ "OVERVIEWTITLE":"You are now done. Review your configuration.", "ACCESSTOKENROLEASSERTION":"Add user roles to the access token", "ACCESSTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the access token.", - "IDTOKENROLEASSERTION":"Add user roles to the id token", - "IDTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the id token." + "IDTOKENROLEASSERTION":"User roles inside ID Token", + "IDTOKENROLEASSERTION_DESCRIPTION":"If selected, the roles of the authenticated user are added to the ID token.", + "IDTOKENUSERINFOASSERTION":"User Info inside ID Token", + "IDTOKENUSERINFOASSERTION_DESCRIPTION":"Enables clients to retrieve profile, email, phone and address claims from ID token.", + "CLOCKSKEW":"Enables clients to handle clock skew of OP and client. The duration (0-5s) will be added to exp claim and subtracted from iats, auth_time and nbf." }, "TOAST": { "REACTIVATED":"Application reactivated.", diff --git a/go.mod b/go.mod index 88c19e2625..5e829050b7 100644 --- a/go.mod +++ b/go.mod @@ -15,8 +15,8 @@ require ( github.com/allegro/bigcache v1.2.1 github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc github.com/caos/logging v0.0.2 - github.com/caos/oidc v0.13.1 - github.com/cockroachdb/cockroach-go/v2 v2.0.8 + github.com/caos/oidc v0.13.2 + github.com/cockroachdb/cockroach-go/v2 v2.1.0 github.com/duo-labs/webauthn v0.0.0-20200714211715-1daaee874e43 github.com/envoyproxy/protoc-gen-validate v0.4.1 github.com/ghodss/yaml v1.0.0 @@ -40,9 +40,8 @@ require ( github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8 // indirect github.com/kevinburke/twilio-go v0.0.0-20200810163702-320748330fac github.com/kr/text v0.2.0 // indirect - github.com/lib/pq v1.8.0 + github.com/lib/pq v1.9.0 github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/nicksnyder/go-i18n/v2 v2.1.1 @@ -69,7 +68,7 @@ require ( golang.org/x/tools v0.0.0-20201103235415-b653051172e4 google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20201103154000-415bd0cd5df6 - google.golang.org/grpc v1.33.1 + google.golang.org/grpc v1.34.0 google.golang.org/protobuf v1.25.0 gopkg.in/square/go-jose.v2 v2.5.1 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect diff --git a/go.sum b/go.sum index d47d213d09..cfe27d1e96 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,8 @@ github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo= github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo= github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0= github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0= -github.com/caos/oidc v0.13.1 h1:8890xd3XJpev5xuU4Dn2l49c1lCA3Qd1xu4KsPSVo38= -github.com/caos/oidc v0.13.1/go.mod h1:dLvfYUiAt9ORfl77L/KkcWuR/N0ll8Ry1nD2ERsamDY= +github.com/caos/oidc v0.13.2 h1:52oP3KB1UrZuwraBTLuwM9ItRIhJQMYOm1J5uQ0sYXw= +github.com/caos/oidc v0.13.2/go.mod h1:dLvfYUiAt9ORfl77L/KkcWuR/N0ll8Ry1nD2ERsamDY= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= @@ -132,10 +132,11 @@ github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 h1:Puu1hUwfps3+1C github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go/v2 v2.0.8 h1:50C/7ptrrfdxDccCjDU0xsdeBca+S0/AYW4Mo8RyzFE= -github.com/cockroachdb/cockroach-go/v2 v2.0.8/go.mod h1:nkf7rUmgPdawp3EwRjXIumihI2AYg9usGNWbJ2hsJqI= +github.com/cockroachdb/cockroach-go/v2 v2.1.0 h1:zicZlBhWZu6wfK7Ezg4Owdc3HamLpRdBllPTT9tb+2k= +github.com/cockroachdb/cockroach-go/v2 v2.1.0/go.mod h1:ilhrLnPDDwGHL+iK2UxQhp1UzUhst8sfItSAgCYwAyg= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -169,6 +170,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.4.1 h1:7dLaJvASGRD7X49jSCSXXHwKPm0ZN9r9kJD+p+vS7dM= @@ -199,7 +201,6 @@ github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80n github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -378,8 +379,12 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= github.com/jackc/pgconn v1.5.0 h1:oFSOilzIZkyg787M1fEmyMfOUUvwj0daqYMfaWwNL4o= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.7.0/go.mod h1:sF/lPpNEMEOp+IYhyQGdAvrG20gWf6A1tKlr0v7JMeA= +github.com/jackc/pgconn v1.7.2/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= @@ -394,31 +399,41 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.1 h1:Rdjp4NFjwHnEslx2b66FfCI2S0LhO4itac3hXz6WX9M= github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.3.0 h1:l8JvKrby3RI7Kg3bYEeU9TA4vqC38QDpFCfcrC7KuN0= -github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik= -github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= -github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.5.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= +github.com/jackc/pgtype v1.6.1/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.6.0 h1:Fh0O9GdlG4gYpjpwOqjdEodJUQM9jzN3Hdv7PN0xmm0= -github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.9.0/go.mod h1:MNGWmViCgqbZck9ujOOBN63gK9XVGILXWCvKLGKmnms= +github.com/jackc/pgx/v4 v4.9.2/go.mod h1:Jt/xJDqjUDUOMSv8VMWPQlCObVgF2XOgqKsW8S4ROYA= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0 h1:musOWczZC/rSbqut475Vfcczg7jJsdUQf0D6oKPLgNU= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= @@ -466,9 +481,10 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU= @@ -476,6 +492,7 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -489,7 +506,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -611,6 +627,7 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea h1:jaXWVFZ98/ihXniiDzqNXQgMSgklX4kjfDWZTE3ZtdU= github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -699,9 +716,11 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= @@ -718,6 +737,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1076,6 +1096,8 @@ google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1123,6 +1145,9 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.0.5/go.mod h1:qrD92UurYzNctBMVCJ8C3VQEjffEuphycXtxOudXNCA= +gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/admin/repository/eventsourcing/eventstore/administrator.go b/internal/admin/repository/eventsourcing/eventstore/administrator.go index 52ab002cac..b36717481e 100644 --- a/internal/admin/repository/eventsourcing/eventstore/administrator.go +++ b/internal/admin/repository/eventsourcing/eventstore/administrator.go @@ -2,10 +2,11 @@ package eventstore import ( "context" + "time" + "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" view_model "github.com/caos/zitadel/internal/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) var dbList = []string{"management", "auth", "authz", "adminapi", "notification"} @@ -47,7 +48,7 @@ func (repo *AdministratorRepo) GetViews() ([]*view_model.View, error) { } func (repo *AdministratorRepo) GetSpoolerDiv(database, view string) int64 { - sequence, err := repo.View.GetCurrentSequence(database, view) + sequence, err := repo.View.GetCurrentSequence(database, view, "") if err != nil { return 0 diff --git a/internal/admin/repository/eventsourcing/eventstore/iam.go b/internal/admin/repository/eventsourcing/eventstore/iam.go index 1904998a13..a35046b606 100644 --- a/internal/admin/repository/eventsourcing/eventstore/iam.go +++ b/internal/admin/repository/eventsourcing/eventstore/iam.go @@ -69,7 +69,7 @@ func (repo *IAMRepository) RemoveIAMMember(ctx context.Context, userID string) e func (repo *IAMRepository) SearchIAMMembers(ctx context.Context, request *iam_model.IAMMemberSearchRequest) (*iam_model.IAMMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestIAMMemberSequence() + sequence, err := repo.View.GetLatestIAMMemberSequence("") logging.Log("EVENT-Slkci").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest iam sequence") members, count, err := repo.View.SearchIAMMembers(request) if err != nil { @@ -194,7 +194,7 @@ func (repo *IAMRepository) ChangeOidcIDPConfig(ctx context.Context, oidcConfig * func (repo *IAMRepository) SearchIDPConfigs(ctx context.Context, request *iam_model.IDPConfigSearchRequest) (*iam_model.IDPConfigSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestIDPConfigSequence() + sequence, err := repo.View.GetLatestIDPConfigSequence("") logging.Log("EVENT-Dk8si").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest idp config sequence") idps, count, err := repo.View.SearchIDPConfigs(request) if err != nil { @@ -298,7 +298,7 @@ func (repo *IAMRepository) ChangeDefaultLoginPolicy(ctx context.Context, policy func (repo *IAMRepository) SearchDefaultIDPProviders(ctx context.Context, request *iam_model.IDPProviderSearchRequest) (*iam_model.IDPProviderSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.AppendAggregateIDQuery(repo.SystemDefaults.IamID) - sequence, err := repo.View.GetLatestIDPProviderSequence() + sequence, err := repo.View.GetLatestIDPProviderSequence("") logging.Log("EVENT-Tuiks").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest iam sequence") providers, count, err := repo.View.SearchIDPProviders(request) if err != nil { diff --git a/internal/admin/repository/eventsourcing/eventstore/org.go b/internal/admin/repository/eventsourcing/eventstore/org.go index 78400eae29..59718f45be 100644 --- a/internal/admin/repository/eventsourcing/eventstore/org.go +++ b/internal/admin/repository/eventsourcing/eventstore/org.go @@ -87,7 +87,7 @@ func (repo *OrgRepo) OrgByID(ctx context.Context, id string) (*org_model.Org, er func (repo *OrgRepo) SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) { query.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestOrgSequence() + sequence, err := repo.View.GetLatestOrgSequence("") logging.Log("EVENT-LXo9w").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest iam sequence") orgs, count, err := repo.View.SearchOrgs(query) if err != nil { diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go index 25f3b69223..ed0e60f6ed 100644 --- a/internal/admin/repository/eventsourcing/handler/handler.go +++ b/internal/admin/repository/eventsourcing/handler/handler.go @@ -3,13 +3,12 @@ package handler import ( "time" - "github.com/caos/zitadel/internal/config/systemdefaults" - iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" + "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/query" + iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" ) @@ -25,6 +24,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -33,31 +38,49 @@ type EventstoreRepos struct { OrgEvents *org_event.OrgEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { return []query.Handler{ - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, - &IamMember{handler: handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount}, - userEvents: repos.UserEvents}, - &IDPConfig{handler: handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount}}, - &LabelPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount}}, - &LoginPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount}}, - &IDPProvider{handler: handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount}, - systemDefaults: defaults, iamEvents: repos.IamEvents, orgEvents: repos.OrgEvents}, - &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - eventstore: eventstore, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, systemDefaults: defaults}, - &PasswordComplexityPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount}}, - &PasswordAgePolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount}}, - &PasswordLockoutPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount}}, - &OrgIAMPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount}}, - &ExternalIDP{handler: handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount}, - orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, systemDefaults: defaults}, + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), + newIAMMember( + handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount, es}, + repos.UserEvents), + newIDPConfig( + handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), + newLabelPolicy( + handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}), + newLoginPolicy( + handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount, es}), + newIDPProvider( + handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, + defaults, + repos.IamEvents, + repos.OrgEvents), + newUser( + handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IamEvents, + defaults), + newPasswordComplexityPolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}), + newPasswordAgePolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}), + newPasswordLockoutPolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount, es}), + newOrgIAMPolicy( + handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), + newExternalIDP( + handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount, es}, + defaults, + repos.IamEvents, + repos.OrgEvents), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } @@ -66,6 +89,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/admin/repository/eventsourcing/handler/iam_member.go b/internal/admin/repository/eventsourcing/handler/iam_member.go index d3c9b77bb6..79aafcf055 100644 --- a/internal/admin/repository/eventsourcing/handler/iam_member.go +++ b/internal/admin/repository/eventsourcing/handler/iam_member.go @@ -4,9 +4,9 @@ import ( "context" "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" @@ -15,40 +15,73 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type IamMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( iamMemberTable = "adminapi.iam_members" ) -func (m *IamMember) ViewModel() string { +type IAMMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newIAMMember(handler handler, userEvents *usr_event.UserEventstore) *IAMMember { + iamMember := &IAMMember{ + handler: handler, + userEvents: userEvents, + } + + iamMember.subscribe() + + return iamMember +} + +func (m *IAMMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + +func (m *IAMMember) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := m.view.GetLatestIAMMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *IAMMember) ViewModel() string { return iamMemberTable } -func (m *IamMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestIAMMemberSequence() +func (m *IAMMember) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate, usr_es_model.UserAggregate} +} + +func (m *IAMMember) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestIAMMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *IamMember) Reduce(event *models.Event) (err error) { +func (m *IAMMember) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: - err = m.processIamMember(event) + err = m.processIAMMember(event) case usr_es_model.UserAggregate: err = m.processUser(event) } return err } -func (m *IamMember) processIamMember(event *models.Event) (err error) { +func (m *IAMMember) processIAMMember(event *es_models.Event) (err error) { member := new(iam_model.IAMMemberView) switch event.Type { case model.IAMMemberAdded: @@ -72,17 +105,17 @@ func (m *IamMember) processIamMember(event *models.Event) (err error) { if err != nil { return err } - return m.view.DeleteIAMMember(event.AggregateID, member.UserID, event.Sequence, event.CreationDate) + return m.view.DeleteIAMMember(event.AggregateID, member.UserID, event) default: - return m.view.ProcessedIAMMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIAMMemberSequence(event) } if err != nil { return err } - return m.view.PutIAMMember(member, member.Sequence, event.CreationDate) + return m.view.PutIAMMember(member, event) } -func (m *IamMember) processUser(event *models.Event) (err error) { +func (m *IAMMember) processUser(event *es_models.Event) (err error) { switch event.Type { case usr_es_model.UserProfileChanged, usr_es_model.UserEmailChanged, @@ -94,7 +127,7 @@ func (m *IamMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return m.view.ProcessedIAMMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIAMMemberSequence(event) } user, err := m.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -103,16 +136,15 @@ func (m *IamMember) processUser(event *models.Event) (err error) { for _, member := range members { m.fillUserData(member, user) } - return m.view.PutIAMMembers(members, event.Sequence, event.CreationDate) + return m.view.PutIAMMembers(members, event) case usr_es_model.UserRemoved: - return m.view.DeleteIAMMembersByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteIAMMembersByUserID(event.AggregateID, event) default: - return m.view.ProcessedIAMMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIAMMemberSequence(event) } - return nil } -func (m *IamMember) fillData(member *iam_model.IAMMemberView) (err error) { +func (m *IAMMember) fillData(member *iam_model.IAMMemberView) (err error) { user, err := m.userEvents.UserByID(context.Background(), member.UserID) if err != nil { return err @@ -121,7 +153,7 @@ func (m *IamMember) fillData(member *iam_model.IAMMemberView) (err error) { return nil } -func (m *IamMember) fillUserData(member *iam_model.IAMMemberView, user *usr_model.User) { +func (m *IAMMember) fillUserData(member *iam_model.IAMMemberView, user *usr_model.User) { member.UserName = user.UserName if user.Human != nil { member.FirstName = user.FirstName @@ -133,11 +165,11 @@ func (m *IamMember) fillUserData(member *iam_model.IAMMemberView, user *usr_mode member.DisplayName = user.Machine.Name } } -func (m *IamMember) OnError(event *models.Event, err error) error { +func (m *IAMMember) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Ld9ow", "id", event.AggregateID).WithError(err).Warn("something went wrong in iammember handler") return spooler.HandleError(event, err, m.view.GetLatestIAMMemberFailedEvent, m.view.ProcessedIAMMemberFailedEvent, m.view.ProcessedIAMMemberSequence, m.errorCountUntilSkip) } -func (m *IamMember) OnSuccess() error { +func (m *IAMMember) OnSuccess() error { return spooler.HandleSuccess(m.view.UpdateIAMMemberSpoolerRunTimestamp) } diff --git a/internal/admin/repository/eventsourcing/handler/idp_config.go b/internal/admin/repository/eventsourcing/handler/idp_config.go index be8d43d7dc..bf0dc97104 100644 --- a/internal/admin/repository/eventsourcing/handler/idp_config.go +++ b/internal/admin/repository/eventsourcing/handler/idp_config.go @@ -2,38 +2,70 @@ package handler import ( "github.com/caos/logging" - iam_model "github.com/caos/zitadel/internal/iam/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) -type IDPConfig struct { - handler -} - const ( idpConfigTable = "adminapi.idp_configs" ) +type IDPConfig struct { + handler + subscription *eventstore.Subscription +} + +func newIDPConfig(handler handler) *IDPConfig { + h := &IDPConfig{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (i *IDPConfig) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} + func (i *IDPConfig) ViewModel() string { return idpConfigTable } -func (i *IDPConfig) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPConfigSequence() +func (i *IDPConfig) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate} +} + +func (i *IDPConfig) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPConfigSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (i *IDPConfig) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := i.view.GetLatestIDPConfigSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (i *IDPConfig) Reduce(event *models.Event) (err error) { +func (i *IDPConfig) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: err = i.processIDPConfig(event) @@ -41,7 +73,7 @@ func (i *IDPConfig) Reduce(event *models.Event) (err error) { return err } -func (i *IDPConfig) processIDPConfig(event *models.Event) (err error) { +func (i *IDPConfig) processIDPConfig(event *es_models.Event) (err error) { idp := new(iam_view_model.IDPConfigView) switch event.Type { case model.IDPConfigAdded: @@ -63,17 +95,17 @@ func (i *IDPConfig) processIDPConfig(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteIDPConfig(idp.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPConfig(idp.IDPConfigID, event) default: - return i.view.ProcessedIDPConfigSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPConfigSequence(event) } if err != nil { return err } - return i.view.PutIDPConfig(idp, idp.Sequence, event.CreationDate) + return i.view.PutIDPConfig(idp, event) } -func (i *IDPConfig) OnError(event *models.Event, err error) error { +func (i *IDPConfig) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Mslo9", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp config handler") return spooler.HandleError(event, err, i.view.GetLatestIDPConfigFailedEvent, i.view.ProcessedIDPConfigFailedEvent, i.view.ProcessedIDPConfigSequence, i.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/idp_providers.go b/internal/admin/repository/eventsourcing/handler/idp_providers.go index 0e27ee11ec..06c44c4c0a 100644 --- a/internal/admin/repository/eventsourcing/handler/idp_providers.go +++ b/internal/admin/repository/eventsourcing/handler/idp_providers.go @@ -2,18 +2,23 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" - "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" - org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" + org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" +) + +const ( + idpProviderTable = "adminapi.idp_providers" ) type IDPProvider struct { @@ -21,27 +26,63 @@ type IDPProvider struct { systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_events.OrgEventstore + subscription *eventstore.Subscription } -const ( - idpProviderTable = "adminapi.idp_providers" -) +func newIDPProvider( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_events.OrgEventstore, +) *IDPProvider { + h := &IDPProvider{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (i *IDPProvider) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *IDPProvider) ViewModel() string { return idpProviderTable } -func (i *IDPProvider) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPProviderSequence() +func (i *IDPProvider) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *IDPProvider) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPProviderSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (i *IDPProvider) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := i.view.GetLatestIDPProviderSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (i *IDPProvider) Reduce(event *models.Event) (err error) { +func (i *IDPProvider) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate, org_es_model.OrgAggregate: err = i.processIdpProvider(event) @@ -49,7 +90,7 @@ func (i *IDPProvider) Reduce(event *models.Event) (err error) { return err } -func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { +func (i *IDPProvider) processIdpProvider(event *es_models.Event) (err error) { provider := new(iam_view_model.IDPProviderView) switch event.Type { case model.LoginPolicyIDPProviderAdded, org_es_model.LoginPolicyIDPProviderAdded: @@ -64,7 +105,7 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event) case model.IDPConfigChanged, org_es_model.IDPConfigChanged: esConfig := new(iam_view_model.IDPConfigView) providerType := iam_model.IDPProviderTypeSystem @@ -83,14 +124,14 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { for _, provider := range providers { i.fillConfigData(provider, config) } - return i.view.PutIDPProviders(event.Sequence, event.CreationDate, providers...) + return i.view.PutIDPProviders(event, providers...) default: - return i.view.ProcessedIDPProviderSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPProviderSequence(event) } if err != nil { return err } - return i.view.PutIDPProvider(provider, provider.Sequence, event.CreationDate) + return i.view.PutIDPProvider(provider, event) } func (i *IDPProvider) fillData(provider *iam_view_model.IDPProviderView) (err error) { @@ -114,7 +155,7 @@ func (i *IDPProvider) fillConfigData(provider *iam_view_model.IDPProviderView, c provider.IDPState = int32(config.State) } -func (i *IDPProvider) OnError(event *models.Event, err error) error { +func (i *IDPProvider) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Msj8c", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp provider handler") return spooler.HandleError(event, err, i.view.GetLatestIDPProviderFailedEvent, i.view.ProcessedIDPProviderFailedEvent, i.view.ProcessedIDPProviderSequence, i.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/label_policy.go b/internal/admin/repository/eventsourcing/handler/label_policy.go index 3f81c0060d..c1604fba69 100644 --- a/internal/admin/repository/eventsourcing/handler/label_policy.go +++ b/internal/admin/repository/eventsourcing/handler/label_policy.go @@ -2,37 +2,69 @@ package handler import ( "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) -type LabelPolicy struct { - handler -} - const ( labelPolicyTable = "adminapi.label_policies" ) +type LabelPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLabelPolicy(handler handler) *LabelPolicy { + h := &LabelPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *LabelPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *LabelPolicy) ViewModel() string { return labelPolicyTable } -func (p *LabelPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestLabelPolicySequence() +func (p *LabelPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate} +} + +func (p *LabelPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestLabelPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *LabelPolicy) Reduce(event *models.Event) (err error) { +func (p *LabelPolicy) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := p.view.GetLatestLabelPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *LabelPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: err = p.processLabelPolicy(event) @@ -40,7 +72,7 @@ func (p *LabelPolicy) Reduce(event *models.Event) (err error) { return err } -func (p *LabelPolicy) processLabelPolicy(event *models.Event) (err error) { +func (p *LabelPolicy) processLabelPolicy(event *es_models.Event) (err error) { policy := new(iam_model.LabelPolicyView) switch event.Type { case model.LabelPolicyAdded: @@ -52,15 +84,15 @@ func (p *LabelPolicy) processLabelPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) default: - return p.view.ProcessedLabelPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedLabelPolicySequence(event) } if err != nil { return err } - return p.view.PutLabelPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutLabelPolicy(policy, event) } -func (p *LabelPolicy) OnError(event *models.Event, err error) error { +func (p *LabelPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Wj8sf", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler") return spooler.HandleError(event, err, p.view.GetLatestLabelPolicyFailedEvent, p.view.ProcessedLabelPolicyFailedEvent, p.view.ProcessedLabelPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/login_policy.go b/internal/admin/repository/eventsourcing/handler/login_policy.go index c24100ca69..032f2a4349 100644 --- a/internal/admin/repository/eventsourcing/handler/login_policy.go +++ b/internal/admin/repository/eventsourcing/handler/login_policy.go @@ -2,36 +2,69 @@ package handler import ( "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) -type LoginPolicy struct { - handler -} - const ( loginPolicyTable = "adminapi.login_policies" ) +type LoginPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLoginPolicy(handler handler) *LoginPolicy { + h := &LoginPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *LoginPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *LoginPolicy) ViewModel() string { return loginPolicyTable } +func (p *LoginPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.IAMAggregate} +} + func (p *LoginPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestLoginPolicySequence() + sequence, err := p.view.GetLatestLoginPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } +func (p *LoginPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestLoginPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *LoginPolicy) Reduce(event *models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate: @@ -56,12 +89,12 @@ func (p *LoginPolicy) processLoginPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) default: - return p.view.ProcessedLoginPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedLoginPolicySequence(event) } if err != nil { return err } - return p.view.PutLoginPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutLoginPolicy(policy, event) } func (p *LoginPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/org.go b/internal/admin/repository/eventsourcing/handler/org.go index eb64e60ea7..02ace32943 100644 --- a/internal/admin/repository/eventsourcing/handler/org.go +++ b/internal/admin/repository/eventsourcing/handler/org.go @@ -3,33 +3,67 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "adminapi.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (o *Org) subscribe() { + o.subscription = o.es.Subscribe(o.AggregateTypes()...) + go func() { + for event := range o.subscription.Events { + query.ReduceEvent(o, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (o *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } return eventsourcing.OrgQuery(sequence.CurrentSequence), nil } +func (o *Org) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) Reduce(event *es_models.Event) error { org := new(org_model.OrgView) @@ -53,10 +87,10 @@ func (o *Org) Reduce(event *es_models.Event) error { return err } default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/admin/repository/eventsourcing/handler/org_iam_policy.go b/internal/admin/repository/eventsourcing/handler/org_iam_policy.go index 99c01e73e1..a98db79c09 100644 --- a/internal/admin/repository/eventsourcing/handler/org_iam_policy.go +++ b/internal/admin/repository/eventsourcing/handler/org_iam_policy.go @@ -2,38 +2,70 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type OrgIAMPolicy struct { - handler -} - const ( orgIAMPolicyTable = "adminapi.org_iam_policies" ) +type OrgIAMPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newOrgIAMPolicy(handler handler) *OrgIAMPolicy { + h := &OrgIAMPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *OrgIAMPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *OrgIAMPolicy) ViewModel() string { return orgIAMPolicyTable } -func (p *OrgIAMPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestOrgIAMPolicySequence() +func (p *OrgIAMPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *OrgIAMPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *OrgIAMPolicy) Reduce(event *models.Event) (err error) { +func (p *OrgIAMPolicy) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *OrgIAMPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.OrgAggregate, iam_es_model.IAMAggregate: err = p.processOrgIAMPolicy(event) @@ -41,7 +73,7 @@ func (p *OrgIAMPolicy) Reduce(event *models.Event) (err error) { return err } -func (p *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { +func (p *OrgIAMPolicy) processOrgIAMPolicy(event *es_models.Event) (err error) { policy := new(iam_model.OrgIAMPolicyView) switch event.Type { case iam_es_model.OrgIAMPolicyAdded, model.OrgIAMPolicyAdded: @@ -53,17 +85,17 @@ func (p *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.OrgIAMPolicyRemoved: - return p.view.DeleteOrgIAMPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeleteOrgIAMPolicy(event.AggregateID, event) default: - return p.view.ProcessedOrgIAMPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedOrgIAMPolicySequence(event) } if err != nil { return err } - return p.view.PutOrgIAMPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutOrgIAMPolicy(policy, event) } -func (p *OrgIAMPolicy) OnError(event *models.Event, err error) error { +func (p *OrgIAMPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Wm8fs", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgIAM policy handler") return spooler.HandleError(event, err, p.view.GetLatestOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/admin/repository/eventsourcing/handler/password_age_policy.go b/internal/admin/repository/eventsourcing/handler/password_age_policy.go index 3608ec446e..7cfa1b2bb2 100644 --- a/internal/admin/repository/eventsourcing/handler/password_age_policy.go +++ b/internal/admin/repository/eventsourcing/handler/password_age_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordAgePolicy struct { - handler -} - const ( passwordAgePolicyTable = "adminapi.password_age_policies" ) +type PasswordAgePolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordAgePolicy(handler handler) *PasswordAgePolicy { + h := &PasswordAgePolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordAgePolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordAgePolicy) ViewModel() string { return passwordAgePolicyTable } +func (p *PasswordAgePolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordAgePolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordAgePolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordAgePolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordAgePolicySequence() + sequence, err := p.view.GetLatestPasswordAgePolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordAgePolicy) processPasswordAgePolicy(event *models.Event) (err e } err = policy.AppendEvent(event) case model.PasswordAgePolicyRemoved: - return p.view.DeletePasswordAgePolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordAgePolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordAgePolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordAgePolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordAgePolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordAgePolicy(policy, event) } func (p *PasswordAgePolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go b/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go index f1d2898218..edab685d23 100644 --- a/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go +++ b/internal/admin/repository/eventsourcing/handler/password_complexity_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordComplexityPolicy struct { - handler -} - const ( passwordComplexityPolicyTable = "adminapi.password_complexity_policies" ) +type PasswordComplexityPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordComplexityPolicy(handler handler) *PasswordComplexityPolicy { + h := &PasswordComplexityPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordComplexityPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordComplexityPolicy) ViewModel() string { return passwordComplexityPolicyTable } +func (p *PasswordComplexityPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordComplexityPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordComplexityPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordComplexityPolicySequence() + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *models } err = policy.AppendEvent(event) case model.PasswordComplexityPolicyRemoved: - return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordComplexityPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordComplexityPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordComplexityPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordComplexityPolicy(policy, event) } func (p *PasswordComplexityPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go b/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go index fd087adda7..99a8393cb8 100644 --- a/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go +++ b/internal/admin/repository/eventsourcing/handler/password_lockout_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordLockoutPolicy struct { - handler -} - const ( passwordLockoutPolicyTable = "adminapi.password_lockout_policies" ) +type PasswordLockoutPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy { + h := &PasswordLockoutPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordLockoutPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordLockoutPolicy) ViewModel() string { return passwordLockoutPolicyTable } +func (p *PasswordLockoutPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordLockoutPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordLockoutPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *models.Event } err = policy.AppendEvent(event) case model.PasswordLockoutPolicyRemoved: - return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordLockoutPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordLockoutPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordLockoutPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordLockoutPolicy(policy, event) } func (p *PasswordLockoutPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/admin/repository/eventsourcing/handler/user.go b/internal/admin/repository/eventsourcing/handler/user.go index f0f26afc4f..eb3a4bd83f 100644 --- a/internal/admin/repository/eventsourcing/handler/user.go +++ b/internal/admin/repository/eventsourcing/handler/user.go @@ -2,6 +2,7 @@ package handler import ( "context" + "github.com/caos/zitadel/internal/config/systemdefaults" iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" @@ -10,6 +11,7 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" @@ -18,29 +20,69 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + userTable = "adminapi.users" +) + type User struct { handler eventstore eventstore.Eventstore orgEvents *org_events.OrgEventstore iamEvents *iam_es.IAMEventstore systemDefaults systemdefaults.SystemDefaults + subscription *eventstore.Subscription } -const ( - userTable = "adminapi.users" -) +func newUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + systemDefaults systemdefaults.SystemDefaults, +) *User { + h := &User{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + systemDefaults: systemDefaults, + } + + h.subscribe() + + return h +} + +func (u *User) subscribe() { + u.subscription = u.es.Subscribe(u.AggregateTypes()...) + go func() { + for event := range u.subscription.Events { + query.ReduceEvent(u, event) + } + }() +} func (u *User) ViewModel() string { return userTable } +func (u *User) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (u *User) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *User) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSequence() + sequence, err := u.view.GetLatestUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -116,14 +158,14 @@ func (u *User) ProcessUser(event *models.Event) (err error) { } err = u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUser(event.AggregateID, event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } if err != nil { return err } - return u.view.PutUser(user, user.Sequence, event.CreationDate) + return u.view.PutUser(user, event) } func (u *User) ProcessOrg(event *models.Event) (err error) { @@ -137,7 +179,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } } @@ -160,7 +202,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.SetLoginNames(policy, org.Domains) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { @@ -185,7 +227,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { diff --git a/internal/admin/repository/eventsourcing/handler/user_external_idps.go b/internal/admin/repository/eventsourcing/handler/user_external_idps.go index cc4d979dc3..f7719e601b 100644 --- a/internal/admin/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/admin/repository/eventsourcing/handler/user_external_idps.go @@ -2,9 +2,11 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" @@ -14,33 +16,74 @@ import ( "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" ) +const ( + externalIDPTable = "adminapi.user_external_idps" +) + type ExternalIDP struct { handler systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - externalIDPTable = "adminapi.user_external_idps" -) +func newExternalIDP( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *ExternalIDP { + h := &ExternalIDP{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (i *ExternalIDP) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *ExternalIDP) ViewModel() string { return externalIDPTable } +func (i *ExternalIDP) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *ExternalIDP) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestExternalIDPSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *ExternalIDP) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestExternalIDPSequence() + sequence, err := i.view.GetLatestExternalIDPSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -68,16 +111,16 @@ func (i *ExternalIDP) processUser(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event) case model.UserRemoved: - return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } if err != nil { return err } - return i.view.PutExternalIDP(externalIDP, externalIDP.Sequence, event.CreationDate) + return i.view.PutExternalIDP(externalIDP, event) } func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { @@ -105,11 +148,10 @@ func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { for _, provider := range exterinalIDPs { i.fillConfigData(provider, config) } - return i.view.PutExternalIDPs(event.Sequence, event.CreationDate, exterinalIDPs...) + return i.view.PutExternalIDPs(event, exterinalIDPs...) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } - return nil } func (i *ExternalIDP) fillData(externalIDP *usr_view_model.ExternalIDPView) error { diff --git a/internal/admin/repository/eventsourcing/spooler/lock.go b/internal/admin/repository/eventsourcing/spooler/lock.go index 76317dc700..f07b190c1c 100644 --- a/internal/admin/repository/eventsourcing/spooler/lock.go +++ b/internal/admin/repository/eventsourcing/spooler/lock.go @@ -2,28 +2,18 @@ package spooler import ( "database/sql" - "time" - es_locker "github.com/caos/zitadel/internal/eventstore/locker" + "time" ) const ( - lockTable = "adminapi.locks" - lockedUntilKey = "locked_until" - lockerIDKey = "locker_id" - objectTypeKey = "object_type" + lockTable = "adminapi.locks" ) type locker struct { dbClient *sql.DB } -type lock struct { - LockerID string `gorm:"column:locker_id;primary_key"` - LockedUntil time.Time `gorm:"column:locked_until"` - ViewName string `gorm:"column:object_type;primary_key"` -} - func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) } diff --git a/internal/admin/repository/eventsourcing/view/external_idps.go b/internal/admin/repository/eventsourcing/view/external_idps.go index 8f21e7de86..384c1d81d0 100644 --- a/internal/admin/repository/eventsourcing/view/external_idps.go +++ b/internal/admin/repository/eventsourcing/view/external_idps.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -33,44 +33,44 @@ func (v *View) SearchExternalIDPs(request *usr_model.ExternalIDPSearchRequest) ( return view.SearchExternalIDPs(v.Db, externalIDPTable, request) } -func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, event *models.Event) error { err := view.PutExternalIDP(v.Db, externalIDPTable, externalIDP) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) PutExternalIDPs(sequence uint64, eventTimestamp time.Time, externalIDPs ...*model.ExternalIDPView) error { +func (v *View) PutExternalIDPs(event *models.Event, externalIDPs ...*model.ExternalIDPView) error { err := view.PutExternalIDPs(v.Db, externalIDPTable, externalIDPs...) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, event *models.Event) error { err := view.DeleteExternalIDP(v.Db, externalIDPTable, externalUserID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDPsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDPsByUserID(userID string, event *models.Event) error { err := view.DeleteExternalIDPsByUserID(v.Db, externalIDPTable, userID) if err != nil { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) GetLatestExternalIDPSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(externalIDPTable) +func (v *View) GetLatestExternalIDPSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(externalIDPTable, aggregateType) } -func (v *View) ProcessedExternalIDPSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(externalIDPTable, eventSequence, eventTimestamp) +func (v *View) ProcessedExternalIDPSequence(event *models.Event) error { + return v.saveCurrentSequence(externalIDPTable, event) } func (v *View) UpdateExternalIDPSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/iam_member.go b/internal/admin/repository/eventsourcing/view/iam_member.go index 250a0042fa..d215ca0b5b 100644 --- a/internal/admin/repository/eventsourcing/view/iam_member.go +++ b/internal/admin/repository/eventsourcing/view/iam_member.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -25,44 +25,44 @@ func (v *View) IAMMembersByUserID(userID string) ([]*model.IAMMemberView, error) return view.IAMMembersByUserID(v.Db, iamMemberTable, userID) } -func (v *View) PutIAMMember(org *model.IAMMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIAMMember(org *model.IAMMemberView, event *models.Event) error { err := view.PutIAMMember(v.Db, iamMemberTable, org) if err != nil { return err } - return v.ProcessedIAMMemberSequence(sequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) PutIAMMembers(members []*model.IAMMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIAMMembers(members []*model.IAMMemberView, event *models.Event) error { err := view.PutIAMMembers(v.Db, iamMemberTable, members...) if err != nil { return err } - return v.ProcessedIAMMemberSequence(sequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) DeleteIAMMember(iamID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIAMMember(iamID, userID string, event *models.Event) error { err := view.DeleteIAMMember(v.Db, iamMemberTable, iamID, userID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIAMMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) DeleteIAMMembersByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIAMMembersByUserID(userID string, event *models.Event) error { err := view.DeleteIAMMembersByUserID(v.Db, iamMemberTable, userID) if err != nil { return err } - return v.ProcessedIAMMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedIAMMemberSequence(event) } -func (v *View) GetLatestIAMMemberSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(iamMemberTable) +func (v *View) GetLatestIAMMemberSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(iamMemberTable, aggregateType) } -func (v *View) ProcessedIAMMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(iamMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIAMMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(iamMemberTable, event) } func (v *View) UpdateIAMMemberSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/idp_configs.go b/internal/admin/repository/eventsourcing/view/idp_configs.go index 197c37d09b..5a120de265 100644 --- a/internal/admin/repository/eventsourcing/view/idp_configs.go +++ b/internal/admin/repository/eventsourcing/view/idp_configs.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -21,28 +21,28 @@ func (v *View) SearchIDPConfigs(request *iam_model.IDPConfigSearchRequest) ([]*m return view.SearchIDPs(v.Db, idpConfigTable, request) } -func (v *View) PutIDPConfig(idp *model.IDPConfigView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPConfig(idp *model.IDPConfigView, event *models.Event) error { err := view.PutIDP(v.Db, idpConfigTable, idp) if err != nil { return err } - return v.ProcessedIDPConfigSequence(sequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) DeleteIDPConfig(idpID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPConfig(idpID string, event *models.Event) error { err := view.DeleteIDP(v.Db, idpConfigTable, idpID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPConfigSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) GetLatestIDPConfigSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpConfigTable) +func (v *View) GetLatestIDPConfigSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpConfigTable, aggregateType) } -func (v *View) ProcessedIDPConfigSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpConfigTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPConfigSequence(event *models.Event) error { + return v.saveCurrentSequence(idpConfigTable, event) } func (v *View) UpdateIDPConfigSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/idp_providers.go b/internal/admin/repository/eventsourcing/view/idp_providers.go index 10b5886945..26a682379d 100644 --- a/internal/admin/repository/eventsourcing/view/idp_providers.go +++ b/internal/admin/repository/eventsourcing/view/idp_providers.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -25,36 +25,36 @@ func (v *View) SearchIDPProviders(request *iam_model.IDPProviderSearchRequest) ( return view.SearchIDPProviders(v.Db, idpProviderTable, request) } -func (v *View) PutIDPProvider(provider *model.IDPProviderView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPProvider(provider *model.IDPProviderView, event *models.Event) error { err := view.PutIDPProvider(v.Db, idpProviderTable, provider) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) PutIDPProviders(sequence uint64, eventTimestamp time.Time, providers ...*model.IDPProviderView) error { +func (v *View) PutIDPProviders(event *models.Event, providers ...*model.IDPProviderView) error { err := view.PutIDPProviders(v.Db, idpProviderTable, providers...) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, event *models.Event) error { err := view.DeleteIDPProvider(v.Db, idpProviderTable, aggregateID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) GetLatestIDPProviderSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpProviderTable) +func (v *View) GetLatestIDPProviderSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpProviderTable, aggregateType) } -func (v *View) ProcessedIDPProviderSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpProviderTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPProviderSequence(event *models.Event) error { + return v.saveCurrentSequence(idpProviderTable, event) } func (v *View) UpdateIDPProviderSpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/label_policies.go b/internal/admin/repository/eventsourcing/view/label_policies.go index f4e921ac1c..9b1a46cee1 100644 --- a/internal/admin/repository/eventsourcing/view/label_policies.go +++ b/internal/admin/repository/eventsourcing/view/label_policies.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -15,20 +15,20 @@ func (v *View) LabelPolicyByAggregateID(aggregateID string) (*model.LabelPolicyV return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTable, aggregateID) } -func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error { err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy) if err != nil { return err } - return v.ProcessedLabelPolicySequence(sequence, eventTimestamp) + return v.ProcessedLabelPolicySequence(event) } -func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(labelPolicyTable) +func (v *View) GetLatestLabelPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(labelPolicyTable, aggregateType) } -func (v *View) ProcessedLabelPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(labelPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLabelPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(labelPolicyTable, event) } func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/login_policies.go b/internal/admin/repository/eventsourcing/view/login_policies.go index 03e4451ffb..7e71983724 100644 --- a/internal/admin/repository/eventsourcing/view/login_policies.go +++ b/internal/admin/repository/eventsourcing/view/login_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LoginPolicyByAggregateID(aggregateID string) (*model.LoginPolicyV return view.GetLoginPolicyByAggregateID(v.Db, loginPolicyTable, aggregateID) } -func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, event *models.Event) error { err := view.PutLoginPolicy(v.Db, loginPolicyTable, policy) if err != nil { return err } - return v.ProcessedLoginPolicySequence(sequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) DeleteLoginPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLoginPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLoginPolicy(v.Db, loginPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLoginPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) GetLatestLoginPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(loginPolicyTable) +func (v *View) GetLatestLoginPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(loginPolicyTable, aggregateType) } -func (v *View) ProcessedLoginPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(loginPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLoginPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(loginPolicyTable, event) } func (v *View) UpdateLoginPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/org.go b/internal/admin/repository/eventsourcing/view/org.go index ee9f9fb113..b5d104a7ed 100644 --- a/internal/admin/repository/eventsourcing/view/org.go +++ b/internal/admin/repository/eventsourcing/view/org.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" org_view "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -20,12 +20,12 @@ func (v *View) SearchOrgs(query *org_model.OrgSearchRequest) ([]*model.OrgView, return org_view.SearchOrgs(v.Db, orgTable, query) } -func (v *View) PutOrg(org *model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -40,10 +40,10 @@ func (v *View) UpdateOrgSpoolerRunTimestamp() error { return v.updateSpoolerRunSequence(orgTable) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } diff --git a/internal/admin/repository/eventsourcing/view/org_iam_policy.go b/internal/admin/repository/eventsourcing/view/org_iam_policy.go index 3168a78152..5d13779e48 100644 --- a/internal/admin/repository/eventsourcing/view/org_iam_policy.go +++ b/internal/admin/repository/eventsourcing/view/org_iam_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) OrgIAMPolicyByAggregateID(aggregateID string) (*model.OrgIAMPolic return view.GetOrgIAMPolicyByAggregateID(v.Db, orgIAMPolicyTable, aggregateID) } -func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, event *models.Event) error { err := view.PutOrgIAMPolicy(v.Db, orgIAMPolicyTable, policy) if err != nil { return err } - return v.ProcessedOrgIAMPolicySequence(sequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) DeleteOrgIAMPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgIAMPolicy(aggregateID string, event *models.Event) error { err := view.DeleteOrgIAMPolicy(v.Db, orgIAMPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedOrgIAMPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) GetLatestOrgIAMPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(orgIAMPolicyTable) +func (v *View) GetLatestOrgIAMPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(orgIAMPolicyTable, aggregateType) } -func (v *View) ProcessedOrgIAMPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgIAMPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgIAMPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(orgIAMPolicyTable, event) } func (v *View) UpdateOrgIAMPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/password_age_policy.go b/internal/admin/repository/eventsourcing/view/password_age_policy.go index 419b537830..b1cbe535c4 100644 --- a/internal/admin/repository/eventsourcing/view/password_age_policy.go +++ b/internal/admin/repository/eventsourcing/view/password_age_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordAgePolicyByAggregateID(aggregateID string) (*model.Passwo return view.GetPasswordAgePolicyByAggregateID(v.Db, passwordAgePolicyTable, aggregateID) } -func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, event *models.Event) error { err := view.PutPasswordAgePolicy(v.Db, passwordAgePolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordAgePolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) DeletePasswordAgePolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordAgePolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordAgePolicy(v.Db, passwordAgePolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordAgePolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) GetLatestPasswordAgePolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordAgePolicyTable) +func (v *View) GetLatestPasswordAgePolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordAgePolicyTable, aggregateType) } -func (v *View) ProcessedPasswordAgePolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordAgePolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordAgePolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordAgePolicyTable, event) } func (v *View) UpdateProcessedPasswordAgePolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/password_complexity_policy.go b/internal/admin/repository/eventsourcing/view/password_complexity_policy.go index 2b1af7d375..44315b9666 100644 --- a/internal/admin/repository/eventsourcing/view/password_complexity_policy.go +++ b/internal/admin/repository/eventsourcing/view/password_complexity_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordComplexityPolicyByAggregateID(aggregateID string) (*model return view.GetPasswordComplexityPolicyByAggregateID(v.Db, passwordComplexityPolicyTable, aggregateID) } -func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, event *models.Event) error { err := view.PutPasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordComplexityPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) DeletePasswordComplexityPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordComplexityPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordComplexityPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) GetLatestPasswordComplexityPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordComplexityPolicyTable) +func (v *View) GetLatestPasswordComplexityPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordComplexityPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordComplexityPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordComplexityPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordComplexityPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordComplexityPolicyTable, event) } func (v *View) UpdatePasswordComplexityPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/password_lockout_policy.go b/internal/admin/repository/eventsourcing/view/password_lockout_policy.go index d20643d37c..805aab3fc3 100644 --- a/internal/admin/repository/eventsourcing/view/password_lockout_policy.go +++ b/internal/admin/repository/eventsourcing/view/password_lockout_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordLockoutPolicyByAggregateID(aggregateID string) (*model.Pa return view.GetPasswordLockoutPolicyByAggregateID(v.Db, passwordLockoutPolicyTable, aggregateID) } -func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, event *models.Event) error { err := view.PutPasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordLockoutPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) DeletePasswordLockoutPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordLockoutPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordLockoutPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) GetLatestPasswordLockoutPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordLockoutPolicyTable) +func (v *View) GetLatestPasswordLockoutPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordLockoutPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordLockoutPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordLockoutPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordLockoutPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordLockoutPolicyTable, event) } func (v *View) UpdatePasswordLockoutPolicySpoolerRunTimestamp() error { diff --git a/internal/admin/repository/eventsourcing/view/sequence.go b/internal/admin/repository/eventsourcing/view/sequence.go index cda4580a6b..5915da9b56 100644 --- a/internal/admin/repository/eventsourcing/view/sequence.go +++ b/internal/admin/repository/eventsourcing/view/sequence.go @@ -1,20 +1,22 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "adminapi.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimeStamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimeStamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) AllCurrentSequences(db string) ([]*repository.CurrentSequence, error) { @@ -22,7 +24,7 @@ func (v *View) AllCurrentSequences(db string) ([]*repository.CurrentSequence, er } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -30,13 +32,16 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } -func (v *View) GetCurrentSequence(db, viewName string) (*repository.CurrentSequence, error) { +func (v *View) GetCurrentSequence(db, viewName, aggregateType string) (*repository.CurrentSequence, error) { sequenceTable := db + ".current_sequences" fullView := db + "." + viewName - return repository.LatestSequence(v.Db, sequenceTable, fullView) + return repository.LatestSequence(v.Db, sequenceTable, fullView, aggregateType) } func (v *View) ClearView(db, viewName string) error { diff --git a/internal/admin/repository/eventsourcing/view/user.go b/internal/admin/repository/eventsourcing/view/user.go index 3de68472cf..9694b41f78 100644 --- a/internal/admin/repository/eventsourcing/view/user.go +++ b/internal/admin/repository/eventsourcing/view/user.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -40,39 +41,36 @@ func (v *View) UserMFAs(userID string) ([]*usr_model.MultiFactor, error) { return view.UserMFAs(v.Db, userTable, userID) } -func (v *View) PutUsers(user []*model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUsers(user []*model.UserView, event *models.Event) error { err := view.PutUsers(v.Db, userTable, user...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) PutUser(user *model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUser(user *model.UserView, event *models.Event) error { err := view.PutUser(v.Db, userTable, user) if err != nil { return err } - if sequence != 0 { - return v.ProcessedUserSequence(sequence, eventTimestamp) - } - return nil + return v.ProcessedUserSequence(event) } -func (v *View) DeleteUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) GetLatestUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userTable) +func (v *View) GetLatestUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userTable, aggregateType) } -func (v *View) ProcessedUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSequence(event *models.Event) error { + return v.saveCurrentSequence(userTable, event) } func (v *View) UpdateUserSpoolerRunTimestamp() error { diff --git a/internal/api/authz/authorization.go b/internal/api/authz/authorization.go index 7ae5e838f4..36b17e5cd8 100644 --- a/internal/api/authz/authorization.go +++ b/internal/api/authz/authorization.go @@ -14,33 +14,39 @@ const ( authenticated = "authenticated" ) -func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID string, verifier *TokenVerifier, authConfig Config, requiredAuthOption Option, method string) (_ context.Context, err error) { +func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID string, verifier *TokenVerifier, authConfig Config, requiredAuthOption Option, method string) (ctxSetter func(context.Context) context.Context, err error) { ctx, span := tracing.NewServerInterceptorSpan(ctx) defer func() { span.EndWithError(err) }() - ctx, err = VerifyTokenAndWriteCtxData(ctx, token, orgID, verifier, method) + ctxData, err := VerifyTokenAndCreateCtxData(ctx, token, orgID, verifier, method) if err != nil { return nil, err } - var perms []string if requiredAuthOption.Permission == authenticated { - return ctx, nil + return func(parent context.Context) context.Context { + return context.WithValue(parent, dataKey, ctxData) + }, nil } - ctx, perms, err = getUserMethodPermissions(ctx, verifier, requiredAuthOption.Permission, authConfig) + requestedPermissions, allPermissions, err := getUserMethodPermissions(ctx, verifier, requiredAuthOption.Permission, authConfig, ctxData) if err != nil { return nil, err } ctx, userPermissionSpan := tracing.NewNamedSpan(ctx, "checkUserPermissions") - err = checkUserPermissions(req, perms, requiredAuthOption) + err = checkUserPermissions(req, requestedPermissions, requiredAuthOption) userPermissionSpan.EndWithError(err) if err != nil { return nil, err } - return ctx, nil + return func(parent context.Context) context.Context { + parent = context.WithValue(parent, dataKey, ctxData) + parent = context.WithValue(parent, allPermissionsKey, allPermissions) + parent = context.WithValue(parent, requestPermissionsKey, requestedPermissions) + return parent + }, nil } func checkUserPermissions(req interface{}, userPerms []string, authOpt Option) error { diff --git a/internal/api/authz/context.go b/internal/api/authz/context.go index 4880a7373e..374ef0c571 100644 --- a/internal/api/authz/context.go +++ b/internal/api/authz/context.go @@ -36,29 +36,36 @@ type Grant struct { Roles []string } -func VerifyTokenAndWriteCtxData(ctx context.Context, token, orgID string, t *TokenVerifier, method string) (_ context.Context, err error) { +func VerifyTokenAndCreateCtxData(ctx context.Context, token, orgID string, t *TokenVerifier, method string) (_ CtxData, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() if orgID != "" { err = t.ExistsOrg(ctx, orgID) if err != nil { - return nil, errors.ThrowPermissionDenied(nil, "AUTH-Bs7Ds", "Organisation doesn't exist") + return CtxData{}, errors.ThrowPermissionDenied(nil, "AUTH-Bs7Ds", "Organisation doesn't exist") } } userID, clientID, agentID, prefLang, err := verifyAccessToken(ctx, token, t, method) if err != nil { - return nil, err + return CtxData{}, err } projectID, origins, err := t.ProjectIDAndOriginsByClientID(ctx, clientID) if err != nil { - return nil, errors.ThrowPermissionDenied(err, "AUTH-GHpw2", "could not read projectid by clientid") + return CtxData{}, errors.ThrowPermissionDenied(err, "AUTH-GHpw2", "could not read projectid by clientid") } if err := checkOrigin(ctx, origins); err != nil { - return nil, err + return CtxData{}, err } - return context.WithValue(ctx, dataKey, CtxData{UserID: userID, OrgID: orgID, ProjectID: projectID, AgentID: agentID, PreferredLanguage: prefLang}), nil + return CtxData{ + UserID: userID, + OrgID: orgID, + ProjectID: projectID, + AgentID: agentID, + PreferredLanguage: prefLang, + }, nil + } func SetCtxData(ctx context.Context, ctxData CtxData) context.Context { diff --git a/internal/api/authz/permissions.go b/internal/api/authz/permissions.go index 8023152ca9..6423f80fd7 100644 --- a/internal/api/authz/permissions.go +++ b/internal/api/authz/permissions.go @@ -7,29 +7,29 @@ import ( "github.com/caos/zitadel/internal/telemetry/tracing" ) -func getUserMethodPermissions(ctx context.Context, t *TokenVerifier, requiredPerm string, authConfig Config) (_ context.Context, _ []string, err error) { +func getUserMethodPermissions(ctx context.Context, t *TokenVerifier, requiredPerm string, authConfig Config, ctxData CtxData) (requestedPermissions, allPermissions []string, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - ctxData := GetCtxData(ctx) if ctxData.IsZero() { return nil, nil, errors.ThrowUnauthenticated(nil, "AUTH-rKLWEH", "context missing") } + + ctx = context.WithValue(ctx, dataKey, ctxData) grant, err := t.ResolveGrant(ctx) if err != nil { return nil, nil, err } if grant == nil { - return context.WithValue(ctx, requestPermissionsKey, []string{}), []string{}, nil + return requestedPermissions, nil, nil } - requestPermissions, allPermissions := mapGrantToPermissions(requiredPerm, grant, authConfig) - ctx = context.WithValue(ctx, allPermissionsKey, allPermissions) - return context.WithValue(ctx, requestPermissionsKey, requestPermissions), requestPermissions, nil + requestedPermissions, allPermissions = mapGrantToPermissions(requiredPerm, grant, authConfig) + return requestedPermissions, allPermissions, nil } -func mapGrantToPermissions(requiredPerm string, grant *Grant, authConfig Config) ([]string, []string) { - requestPermissions := make([]string, 0) - allPermissions := make([]string, 0) +func mapGrantToPermissions(requiredPerm string, grant *Grant, authConfig Config) (requestPermissions, allPermissions []string) { + requestPermissions = make([]string, 0) + allPermissions = make([]string, 0) for _, role := range grant.Roles { requestPermissions, allPermissions = mapRoleToPerm(requiredPerm, role, authConfig, requestPermissions, allPermissions) } diff --git a/internal/api/authz/permissions_test.go b/internal/api/authz/permissions_test.go index 92e4407706..69877fd4cb 100644 --- a/internal/api/authz/permissions_test.go +++ b/internal/api/authz/permissions_test.go @@ -49,7 +49,7 @@ func equalStringArray(a, b []string) bool { func Test_GetUserMethodPermissions(t *testing.T) { type args struct { - ctx context.Context + ctxData CtxData verifier *TokenVerifier requiredPerm string authConfig Config @@ -64,7 +64,7 @@ func Test_GetUserMethodPermissions(t *testing.T) { { name: "Empty Context", args: args{ - ctx: getTestCtx("", ""), + ctxData: CtxData{}, verifier: Start(&testVerifier{grant: &Grant{ Roles: []string{"ORG_OWNER"}, }}), @@ -89,7 +89,7 @@ func Test_GetUserMethodPermissions(t *testing.T) { { name: "No Grants", args: args{ - ctx: getTestCtx("", ""), + ctxData: CtxData{}, verifier: Start(&testVerifier{grant: &Grant{}}), requiredPerm: "project.read", authConfig: Config{ @@ -110,9 +110,9 @@ func Test_GetUserMethodPermissions(t *testing.T) { { name: "Get Permissions", args: args{ - ctx: getTestCtx("userID", "orgID"), + ctxData: CtxData{UserID: "userID", OrgID: "orgID"}, verifier: Start(&testVerifier{grant: &Grant{ - Roles: []string{"ORG_OWNER"}, + Roles: []string{"IAM_OWNER"}, }}), requiredPerm: "project.read", authConfig: Config{ @@ -133,7 +133,7 @@ func Test_GetUserMethodPermissions(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, perms, err := getUserMethodPermissions(tt.args.ctx, tt.args.verifier, tt.args.requiredPerm, tt.args.authConfig) + _, perms, err := getUserMethodPermissions(context.Background(), tt.args.verifier, tt.args.requiredPerm, tt.args.authConfig, tt.args.ctxData) if tt.wantErr && err == nil { t.Errorf("got wrong result, should get err: actual: %v ", err) diff --git a/internal/api/grpc/admin/administrator_converter.go b/internal/api/grpc/admin/administrator_converter.go index 1f93d2496c..edd1691eaa 100644 --- a/internal/api/grpc/admin/administrator_converter.go +++ b/internal/api/grpc/admin/administrator_converter.go @@ -28,8 +28,8 @@ func failedEventsFromModel(failedEvents []*view_model.FailedEvent) []*admin.Fail func viewFromModel(view *view_model.View) *admin.View { eventTimestamp, err := ptypes.TimestampProto(view.EventTimestamp) logging.Log("GRPC-KSo03").OnError(err).Debug("unable to parse timestamp") - lastSpool, err := ptypes.TimestampProto(view.EventTimestamp) - logging.Log("GRPC-KSo03").OnError(err).Debug("unable to parse timestamp") + lastSpool, err := ptypes.TimestampProto(view.LastSuccessfulSpoolerRun) + logging.Log("GRPC-0oP87").OnError(err).Debug("unable to parse timestamp") return &admin.View{ Database: view.Database, diff --git a/internal/api/grpc/auth/user.go b/internal/api/grpc/auth/user.go index e2d94e9d47..f00ef7f872 100644 --- a/internal/api/grpc/auth/user.go +++ b/internal/api/grpc/auth/user.go @@ -2,6 +2,7 @@ package auth import ( "context" + "github.com/golang/protobuf/ptypes/empty" "github.com/caos/zitadel/pkg/grpc/auth" @@ -162,6 +163,9 @@ func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Emp func (s *Server) AddMyMfaU2F(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) { u2f, err := s.repo.AddMyMFAU2F(ctx) + if err != nil { + return nil, err + } return verifyWebAuthNFromModel(u2f), err } @@ -175,8 +179,19 @@ func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) ( return &empty.Empty{}, err } +func (s *Server) GetMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNTokens, err error) { + tokens, err := s.repo.GetMyPasswordless(ctx) + if err != nil { + return nil, err + } + return webAuthNTokensFromModel(tokens), err +} + func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) { u2f, err := s.repo.AddMyPasswordless(ctx) + if err != nil { + return nil, err + } return verifyWebAuthNFromModel(u2f), err } diff --git a/internal/api/grpc/auth/user_converter.go b/internal/api/grpc/auth/user_converter.go index 21e56f79fa..3251320201 100644 --- a/internal/api/grpc/auth/user_converter.go +++ b/internal/api/grpc/auth/user_converter.go @@ -436,3 +436,19 @@ func verifyWebAuthNFromModel(u2f *usr_model.WebAuthNToken) *auth.WebAuthNRespons State: mfaStateFromModel(u2f.State), } } + +func webAuthNTokensFromModel(tokens []*usr_model.WebAuthNToken) *auth.WebAuthNTokens { + result := make([]*auth.WebAuthNToken, len(tokens)) + for i, token := range tokens { + result[i] = webAuthNTokenFromModel(token) + } + return &auth.WebAuthNTokens{Tokens: result} +} + +func webAuthNTokenFromModel(token *usr_model.WebAuthNToken) *auth.WebAuthNToken { + return &auth.WebAuthNToken{ + Id: token.WebAuthNTokenID, + Name: token.WebAuthNTokenName, + State: mfaStateFromModel(token.State), + } +} diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index 031503118c..b7151f070d 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -2,9 +2,11 @@ package management import ( "context" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/pkg/grpc/management" - "github.com/golang/protobuf/ptypes/empty" ) func (s *Server) GetUserByID(ctx context.Context, id *management.UserID) (*management.UserView, error) { @@ -226,6 +228,24 @@ func (s *Server) RemoveMfaOTP(ctx context.Context, userID *management.UserID) (* return &empty.Empty{}, err } +func (s *Server) RemoveMfaU2F(ctx context.Context, webAuthNTokenID *management.WebAuthNTokenID) (*empty.Empty, error) { + err := s.user.RemoveU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id) + return &empty.Empty{}, err +} + +func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID) (_ *management.WebAuthNTokens, err error) { + tokens, err := s.user.GetPasswordless(ctx, userID.Id) + if err != nil { + return nil, err + } + return webAuthNTokensFromModel(tokens), err +} + +func (s *Server) RemovePasswordless(ctx context.Context, id *management.WebAuthNTokenID) (*empty.Empty, error) { + err := s.user.RemovePasswordless(ctx, id.UserId, id.Id) + return &empty.Empty{}, err +} + func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) { request := userMembershipSearchRequestsToModel(in) request.AppendUserIDQuery(in.UserId) diff --git a/internal/api/grpc/management/user_converter.go b/internal/api/grpc/management/user_converter.go index 645a07c0ea..ced79f4948 100644 --- a/internal/api/grpc/management/user_converter.go +++ b/internal/api/grpc/management/user_converter.go @@ -2,14 +2,15 @@ package management import ( "encoding/json" + "github.com/caos/logging" - "github.com/caos/zitadel/internal/model" "github.com/golang/protobuf/ptypes" "golang.org/x/text/language" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/structpb" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/model" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/pkg/grpc/management" "github.com/caos/zitadel/pkg/grpc/message" @@ -504,6 +505,7 @@ func mfaFromModel(mfa *usr_model.MultiFactor) *management.UserMultiFactor { State: mfaStateFromModel(mfa.State), Type: mfaTypeFromModel(mfa.Type), Attribute: mfa.Attribute, + Id: mfa.ID, } } @@ -627,3 +629,19 @@ func userChangesToMgtAPI(changes *usr_model.UserChanges) (_ []*management.Change return result } + +func webAuthNTokensFromModel(tokens []*usr_model.WebAuthNToken) *management.WebAuthNTokens { + result := make([]*management.WebAuthNToken, len(tokens)) + for i, token := range tokens { + result[i] = webAuthNTokenFromModel(token) + } + return &management.WebAuthNTokens{Tokens: result} +} + +func webAuthNTokenFromModel(token *usr_model.WebAuthNToken) *management.WebAuthNToken { + return &management.WebAuthNToken{ + Id: token.WebAuthNTokenID, + Name: token.WebAuthNTokenName, + State: mfaStateFromModel(token.State), + } +} diff --git a/internal/api/grpc/server/middleware/auth_interceptor.go b/internal/api/grpc/server/middleware/auth_interceptor.go index 347e0b18f9..165be2a6f9 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor.go +++ b/internal/api/grpc/server/middleware/auth_interceptor.go @@ -25,20 +25,20 @@ func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, return handler(ctx, req) } - ctx, span := tracing.NewServerInterceptorSpan(ctx) + authCtx, span := tracing.NewServerInterceptorSpan(ctx) defer func() { span.EndWithError(err) }() - authToken := grpc_util.GetAuthorizationHeader(ctx) + authToken := grpc_util.GetAuthorizationHeader(authCtx) if authToken == "" { return nil, status.Error(codes.Unauthenticated, "auth header missing") } - orgID := grpc_util.GetHeader(ctx, http.ZitadelOrgID) + orgID := grpc_util.GetHeader(authCtx, http.ZitadelOrgID) - ctx, err = authz.CheckUserAuthorization(ctx, req, authToken, orgID, verifier, authConfig, authOpt, info.FullMethod) + ctxSetter, err := authz.CheckUserAuthorization(authCtx, req, authToken, orgID, verifier, authConfig, authOpt, info.FullMethod) if err != nil { return nil, err } span.End() - return handler(ctx, req) + return handler(ctxSetter(ctx), req) } diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 760c8a007d..39fffdba0d 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -2,17 +2,16 @@ package oidc import ( "context" - "github.com/caos/zitadel/internal/auth_request/model" "strings" - "golang.org/x/text/language" - "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/op" + "golang.org/x/text/language" + "gopkg.in/square/go-jose.v2" "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/http" + "github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/errors" proj_model "github.com/caos/zitadel/internal/project/model" @@ -155,7 +154,7 @@ func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID, applicati roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix)) } if strings.HasPrefix(scope, model.OrgDomainPrimaryScope) { - userInfo.AppendClaims(model.OrgDomainPrimaryScope, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)) + userInfo.AppendClaims(model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)) } } } @@ -180,7 +179,7 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie if strings.HasPrefix(scope, ScopeProjectRolePrefix) { roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix)) } else if strings.HasPrefix(scope, model.OrgDomainPrimaryScope) { - claims = map[string]interface{}{model.OrgDomainPrimaryScope: strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)} + claims = appendClaim(claims, model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope)) } } if len(roles) == 0 || clientID == "" { @@ -191,7 +190,7 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie return nil, err } if len(projectRoles) > 0 { - claims = map[string]interface{}{ClaimProjectRoles: projectRoles} + claims = appendClaim(claims, ClaimProjectRoles, projectRoles) } return claims, err } @@ -240,3 +239,11 @@ func getGender(gender user_model.Gender) string { } return "" } + +func appendClaim(claims map[string]interface{}, claim string, value interface{}) map[string]interface{} { + if claims == nil { + claims = make(map[string]interface{}) + } + claims[claim] = value + return claims +} diff --git a/internal/auth/repository/auth_request.go b/internal/auth/repository/auth_request.go index 3aaab44596..8425c68a06 100644 --- a/internal/auth/repository/auth_request.go +++ b/internal/auth/repository/auth_request.go @@ -30,6 +30,4 @@ type AuthRequestRepository interface { LinkExternalUsers(ctx context.Context, authReqID, userAgentID string, info *model.BrowserInfo) error AutoRegisterExternalUser(ctx context.Context, user *user_model.User, externalIDP *user_model.ExternalIDP, member *org_model.OrgMember, authReqID, userAgentID, resourceOwner string, info *model.BrowserInfo) error ResetLinkingUsers(ctx context.Context, authReqID, userAgentID string) error - - GetOrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 68189e5890..92ef6aada6 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -110,6 +110,9 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *mod } request.Audience = appIDs request.AppendAudIfNotExisting(app.ProjectID) + if err := setOrgID(repo.OrgViewProvider, request); err != nil { + return nil, err + } if request.LoginHint != "" { err = repo.checkLoginName(ctx, request, request.LoginHint) logging.LogWithFields("EVENT-aG311", "login name", request.LoginHint, "id", request.ID, "applicationID", request.ApplicationID, "traceID", tracing.TraceIDFromCtx(ctx)).OnError(err).Debug("login hint invalid") @@ -238,6 +241,9 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge if err != nil { return err } + if request.RequestedOrgID != "" && request.RequestedOrgID != user.ResourceOwner { + return errors.ThrowPreconditionFailed(nil, "EVENT-fJe2a", "Errors.User.NotAllowedOrg") + } request.SetUserInfo(user.ID, user.PreferredLoginName, user.DisplayName, user.ResourceOwner) return repo.AuthRequests.UpdateAuthRequest(ctx, request) } @@ -442,16 +448,9 @@ func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context, } func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model.AuthRequest) error { - orgID := request.UserOrgID + orgID := request.RequestedOrgID if orgID == "" { - primaryDomain := request.GetScopeOrgPrimaryDomain() - if primaryDomain != "" { - org, err := repo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - return err - } - orgID = org.ID - } + orgID = request.UserOrgID } if orgID == "" { orgID = repo.IAMID @@ -469,19 +468,9 @@ func (repo *AuthRequestRepo) fillLoginPolicy(ctx context.Context, request *model } func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model.AuthRequest, loginName string) (err error) { - primaryDomain := request.GetScopeOrgPrimaryDomain() - orgID := "" - if primaryDomain != "" { - org, err := repo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - return err - } - orgID = org.ID - } - user := new(user_view_model.UserView) - if orgID != "" { - user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, orgID) + if request.RequestedOrgID != "" { + user, err = repo.View.UserByLoginNameAndResourceOwner(loginName, request.RequestedOrgID) } else { user, err = repo.View.UserByLoginName(loginName) if err == nil { @@ -499,14 +488,6 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *model. return nil } -func (repo AuthRequestRepo) GetOrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) { - org, err := repo.OrgViewProvider.OrgByPrimaryDomain(primaryDomain) - if err != nil { - return nil, err - } - return org_view_model.OrgToModel(org), nil -} - func (repo AuthRequestRepo) checkLoginPolicyWithResourceOwner(ctx context.Context, request *model.AuthRequest, user *user_view_model.UserView) error { loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, user.ResourceOwner) if err != nil { @@ -537,15 +518,9 @@ func (repo *AuthRequestRepo) checkSelectedExternalIDP(request *model.AuthRequest } func (repo *AuthRequestRepo) checkExternalUserLogin(request *model.AuthRequest, idpConfigID, externalUserID string) (err error) { - primaryDomain := request.GetScopeOrgPrimaryDomain() externalIDP := new(user_view_model.ExternalIDPView) - org := new(org_model.OrgView) - if primaryDomain != "" { - org, err = repo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - return err - } - externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, org.ID) + if request.RequestedOrgID != "" { + externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigIDAndResourceOwner(externalUserID, idpConfigID, request.RequestedOrgID) } else { externalIDP, err = repo.View.ExternalIDPByExternalUserIDAndIDPConfigID(externalUserID, idpConfigID) } @@ -653,10 +628,11 @@ func (repo *AuthRequestRepo) usersForUserSelection(request *model.AuthRequest) ( users := make([]model.UserSelection, len(userSessions)) for i, session := range userSessions { users[i] = model.UserSelection{ - UserID: session.UserID, - DisplayName: session.DisplayName, - LoginName: session.LoginName, - UserSessionState: session.State, + UserID: session.UserID, + DisplayName: session.DisplayName, + LoginName: session.LoginName, + UserSessionState: session.State, + SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner, } } return users, nil @@ -667,24 +643,28 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user return &model.InitUserStep{PasswordSet: user.PasswordSet} } - if user.IsPasswordlessReady() { - if !checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { - return &model.PasswordlessStep{} + var step model.NextStep + if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() { + if checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { + request.AuthTime = userSession.PasswordlessVerification + return nil } - request.AuthTime = userSession.PasswordlessVerification - return nil + step = &model.PasswordlessStep{} } if !user.PasswordSet { return &model.InitPasswordStep{} } - if !checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) { - return &model.PasswordStep{} + if checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) { + request.PasswordVerified = true + request.AuthTime = userSession.PasswordVerification + return nil } - request.PasswordVerified = true - request.AuthTime = userSession.PasswordVerification - return nil + if step != nil { + return step + } + return &model.PasswordStep{} } func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *model.AuthRequest, user *user_model.UserView) (model.NextStep, bool, error) { @@ -753,6 +733,21 @@ func (repo *AuthRequestRepo) getLoginPolicy(ctx context.Context, orgID string) ( return iam_es_model.LoginPolicyViewToModel(policy), err } +func setOrgID(orgViewProvider orgViewProvider, request *model.AuthRequest) error { + primaryDomain := request.GetScopeOrgPrimaryDomain() + if primaryDomain == "" { + return nil + } + + org, err := orgViewProvider.OrgByPrimaryDomain(primaryDomain) + if err != nil { + return err + } + request.RequestedOrgID = org.ID + request.RequestedOrgName = org.Name + return nil +} + func getLoginPolicyIDPProviders(provider idpProviderViewProvider, iamID, orgID string, defaultPolicy bool) ([]*iam_model.IDPProviderView, error) { if defaultPolicy { idpProviders, err := provider.IDPProvidersByAggregateIDAndState(iamID, iam_model.IDPConfigStateActive) @@ -824,9 +819,8 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve case es_model.UserRemoved: return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dG2fe", "Errors.User.NotActive") } - if err := sessionCopy.AppendEvent(event); err != nil { - return user_view_model.UserSessionToModel(&sessionCopy), nil - } + err := sessionCopy.AppendEvent(event) + logging.Log("EVENT-qbhj3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("error appending event") } return user_view_model.UserSessionToModel(&sessionCopy), nil } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index c3de87ad30..f6085c1ebf 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -56,8 +56,9 @@ type mockViewUserSession struct { } type mockUser struct { - UserID string - LoginName string + UserID string + LoginName string + ResourceOwner string } func (m *mockViewUserSession) UserSessionByIDs(string, string) (*user_view_model.UserSessionView, error) { @@ -74,8 +75,9 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string) ([]*user_view_model. sessions := make([]*user_view_model.UserSessionView, len(m.Users)) for i, user := range m.Users { sessions[i] = &user_view_model.UserSessionView{ - UserID: user.UserID, - LoginName: user.LoginName, + UserID: user.UserID, + LoginName: user.LoginName, + ResourceOwner: user.ResourceOwner, } } return sessions, nil @@ -270,10 +272,12 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { { "id1", "loginname1", + "orgID1", }, { "id2", "loginname2", + "orgID2", }, }, }, @@ -285,12 +289,52 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { &model.SelectUserStep{ Users: []model.UserSelection{ { - UserID: "id1", - LoginName: "loginname1", + UserID: "id1", + LoginName: "loginname1", + SelectionPossible: true, }, { - UserID: "id2", - LoginName: "loginname2", + UserID: "id2", + LoginName: "loginname2", + SelectionPossible: true, + }, + }, + }}, + nil, + }, + { + "user not set, primary domain set, prompt select account, login and select account steps", + fields{ + userSessionViewProvider: &mockViewUserSession{ + Users: []mockUser{ + { + "id1", + "loginname1", + "orgID1", + }, + { + "id2", + "loginname2", + "orgID2", + }, + }, + }, + userEventProvider: &mockEventUser{}, + }, + args{&model.AuthRequest{Prompt: model.PromptSelectAccount, RequestedOrgID: "orgID1"}, false}, + []model.NextStep{ + &model.LoginStep{}, + &model.SelectUserStep{ + Users: []model.UserSelection{ + { + UserID: "id1", + LoginName: "loginname1", + SelectionPossible: true, + }, + { + UserID: "id2", + LoginName: "loginname2", + SelectionPossible: false, }, }, }}, @@ -386,7 +430,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { userEventProvider: &mockEventUser{}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false}, []model.NextStep{&model.PasswordStep{}}, nil, }, @@ -431,7 +475,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, MultiFactorCheckLifeTime: 10 * time.Hour, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{PasswordlessType: iam_model.PasswordlessTypeAllowed}}, false}, []model.NextStep{&model.PasswordlessStep{}}, nil, }, @@ -456,7 +500,8 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { args{&model.AuthRequest{ UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{ - MultiFactors: []iam_model.MultiFactorType{iam_model.MultiFactorTypeU2FWithPIN}, + PasswordlessType: iam_model.PasswordlessTypeAllowed, + MultiFactors: []iam_model.MultiFactorType{iam_model.MultiFactorTypeU2FWithPIN}, }, }, false}, []model.NextStep{&model.VerifyEMailStep{}}, @@ -470,7 +515,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { userEventProvider: &mockEventUser{}, orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false}, []model.NextStep{&model.InitPasswordStep{}}, nil, }, @@ -534,7 +579,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, PasswordCheckLifeTime: 10 * 24 * time.Hour, }, - args{&model.AuthRequest{UserID: "UserID"}, false}, + args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false}, []model.NextStep{&model.PasswordStep{}}, nil, }, @@ -566,6 +611,35 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { []model.NextStep{&model.RedirectToCallbackStep{}}, nil, }, + { + "password verified, passwordless set up, mfa not verified, mfa check step", + fields{ + userSessionViewProvider: &mockViewUserSession{ + PasswordVerification: time.Now().UTC().Add(-5 * time.Minute), + }, + userViewProvider: &mockViewUser{ + PasswordSet: true, + PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}}, + OTPState: int32(user_model.MFAStateReady), + MFAMaxSetUp: int32(model.MFALevelMultiFactor), + }, + userEventProvider: &mockEventUser{}, + orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, + PasswordCheckLifeTime: 10 * 24 * time.Hour, + SecondFactorCheckLifeTime: 18 * time.Hour, + }, + args{ + &model.AuthRequest{ + UserID: "UserID", + LoginPolicy: &iam_model.LoginPolicyView{ + SecondFactors: []iam_model.SecondFactorType{iam_model.SecondFactorTypeOTP}, + }, + }, false}, + []model.NextStep{&model.MFAVerificationStep{ + MFAProviders: []model.MFAType{model.MFATypeOTP}, + }}, + nil, + }, { "mfa not verified, mfa check step", fields{ @@ -843,6 +917,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { args{ &model.AuthRequest{ UserID: "UserID", + LoginPolicy: &iam_model.LoginPolicyView{}, SelectedIDPConfigID: "IDPConfigID", LinkingUsers: []*model.ExternalUser{{IDPConfigID: "IDPConfigID", ExternalUserID: "UserID", DisplayName: "DisplayName"}}, }, false}, diff --git a/internal/auth/repository/eventsourcing/eventstore/org.go b/internal/auth/repository/eventsourcing/eventstore/org.go index c5f3ecced0..9339b1be70 100644 --- a/internal/auth/repository/eventsourcing/eventstore/org.go +++ b/internal/auth/repository/eventsourcing/eventstore/org.go @@ -36,7 +36,7 @@ type OrgRepository struct { func (repo *OrgRepository) SearchOrgs(ctx context.Context, request *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestOrgSequence() + sequence, err := repo.View.GetLatestOrgSequence("") logging.Log("EVENT-7Udhz").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest org sequence") members, count, err := repo.View.SearchOrgs(request) if err != nil { diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index 7fffb728d8..392ff2a793 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -109,7 +109,7 @@ func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profil func (repo *UserRepo) SearchMyExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, seqErr := repo.View.GetLatestExternalIDPSequence() + sequence, seqErr := repo.View.GetLatestExternalIDPSequence("") logging.Log("EVENT-5Jsi8").OnError(seqErr).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest user sequence") request.AppendUserQuery(authz.GetCtxData(ctx).UserID) externalIDPS, count, err := repo.View.SearchExternalIDPs(request) @@ -303,11 +303,26 @@ func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error { } func (repo *UserRepo) AddMFAU2F(ctx context.Context, userID string) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddU2F(ctx, userID, true) + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddU2F(ctx, userID, accountName, true) } func (repo *UserRepo) AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddU2F(ctx, authz.GetCtxData(ctx).UserID, false) + userID := authz.GetCtxData(ctx).UserID + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-Ghwl1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddU2F(ctx, userID, accountName, false) } func (repo *UserRepo) VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error { @@ -326,12 +341,35 @@ func (repo *UserRepo) RemoveMyMFAU2F(ctx context.Context, webAuthNTokenID string return repo.UserEvents.RemoveU2FToken(ctx, authz.GetCtxData(ctx).UserID, webAuthNTokenID) } +func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error) { + return repo.UserEvents.GetPasswordless(ctx, userID) +} + func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddPasswordless(ctx, userID, true) + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-Vj2k1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddPasswordless(ctx, userID, accountName, true) +} + +func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) { + return repo.UserEvents.GetPasswordless(ctx, authz.GetCtxData(ctx).UserID) } func (repo *UserRepo) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) { - return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, false) + userID := authz.GetCtxData(ctx).UserID + accountName := "" + user, err := repo.UserByID(ctx, userID) + if err != nil { + logging.Log("EVENT-AEq21").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname") + } else { + accountName = user.PreferredLoginName + } + return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, accountName, false) } func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error { diff --git a/internal/auth/repository/eventsourcing/eventstore/user_grant.go b/internal/auth/repository/eventsourcing/eventstore/user_grant.go index cba39e4314..d9ba6bc842 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/auth/repository/eventsourcing/eventstore/user_grant.go @@ -29,7 +29,7 @@ type UserGrantRepo struct { func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, err := repo.View.GetLatestUserGrantSequence() + sequence, err := repo.View.GetLatestUserGrantSequence("") logging.Log("EVENT-Hd7s3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest user grant sequence") request.Queries = append(request.Queries, &grant_model.UserGrantSearchQuery{Key: grant_model.UserGrantSearchKeyUserID, Method: global_model.SearchMethodEquals, Value: authz.GetCtxData(ctx).UserID}) grants, count, err := repo.View.SearchUserGrants(request) diff --git a/internal/auth/repository/eventsourcing/handler/application.go b/internal/auth/repository/eventsourcing/handler/application.go index 377e0b0499..5a8d5d41ff 100644 --- a/internal/auth/repository/eventsourcing/handler/application.go +++ b/internal/auth/repository/eventsourcing/handler/application.go @@ -5,7 +5,9 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -13,19 +15,52 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Application struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( applicationTable = "auth.applications" ) +type Application struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newApplication(handler handler, projectEvents *proj_event.ProjectEventstore) *Application { + h := &Application{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (a *Application) subscribe() { + a.subscription = a.es.Subscribe(a.AggregateTypes()...) + go func() { + for event := range a.subscription.Events { + query.ReduceEvent(a, event) + } + }() +} + func (a *Application) ViewModel() string { return applicationTable } +func (_ *Application) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (a *Application) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := a.view.GetLatestApplicationSequence() + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (a *Application) EventQuery() (*models.SearchQuery, error) { sequence, err := a.view.GetLatestApplicationSequence() if err != nil { @@ -65,30 +100,33 @@ func (a *Application) Reduce(event *models.Event) (err error) { if err != nil { return err } - return a.view.DeleteApplication(app.ID, event.Sequence, event.CreationDate) + return a.view.DeleteApplication(app.ID, event) case es_model.ProjectChanged: apps, err := a.view.ApplicationsByProjectID(event.AggregateID) if err != nil { return err } if len(apps) == 0 { - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } for _, app := range apps { if err := app.AppendEvent(event); err != nil { return err } } - return a.view.PutApplications(apps, event.Sequence, event.CreationDate) + return a.view.PutApplications(apps, event) case es_model.ProjectRemoved: - return a.view.DeleteApplicationsByProjectID(event.AggregateID) + err = a.view.DeleteApplicationsByProjectID(event.AggregateID) + if err == nil { + return a.view.ProcessedApplicationSequence(event) + } default: - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } if err != nil { return err } - return a.view.PutApplication(app, event.CreationDate) + return a.view.PutApplication(app, event) } func (a *Application) OnError(event *models.Event, spoolerError error) error { diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index d641ec39db..c5825c5abe 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -26,6 +26,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -35,39 +41,65 @@ type EventstoreRepos struct { IamEvents *iam_events.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { return []query.Handler{ - &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, iamID: systemDefaults.IamID}, - &UserSession{handler: handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount}, userEvents: repos.UserEvents}, - &UserMembership{handler: handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount}, orgEvents: repos.OrgEvents, projectEvents: repos.ProjectEvents}, - &Token{handler: handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount}, ProjectEvents: repos.ProjectEvents}, - &Key{handler: handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount}}, - &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, projectEvents: repos.ProjectEvents}, - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, - &UserGrant{ - handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, - eventstore: eventstore, - userEvents: repos.UserEvents, - orgEvents: repos.OrgEvents, - projectEvents: repos.ProjectEvents, - iamEvents: repos.IamEvents, - iamID: systemDefaults.IamID}, - &MachineKeys{handler: handler{view, bulkLimit, configs.cycleDuration("MachineKey"), errorCount}}, - &LoginPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount}}, - &IDPConfig{handler: handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount}}, - &IDPProvider{handler: handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount}, systemDefaults: systemDefaults, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents}, - &ExternalIDP{handler: handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount}, systemDefaults: systemDefaults, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents}, - &PasswordComplexityPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount}}, - &OrgIAMPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount}}, - &ProjectRole{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount}, projectEvents: repos.ProjectEvents}, + newUser( + handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IamEvents, + systemDefaults.IamID), + newUserSession( + handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount, es}, + repos.UserEvents), + newUserMembership( + handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}, + repos.OrgEvents, + repos.ProjectEvents), + newToken( + handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount, es}, + repos.ProjectEvents), + newKey( + handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount, es}), + newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}, + repos.ProjectEvents), + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), + newUserGrant( + handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, + repos.ProjectEvents, + repos.UserEvents, + repos.OrgEvents, + repos.IamEvents, + systemDefaults.IamID), + newMachineKeys( + handler{view, bulkLimit, configs.cycleDuration("MachineKey"), errorCount, es}), + newLoginPolicy( + handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount, es}), + newIDPConfig( + handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), + newIDPProvider( + handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, + systemDefaults, + repos.IamEvents, + repos.OrgEvents), + newExternalIDP( + handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount, es}, + systemDefaults, + repos.IamEvents, + repos.OrgEvents), + newPasswordComplexityPolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}), + newOrgIAMPolicy( + handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), + newProjectRole(handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount, es}, + repos.ProjectEvents), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } @@ -76,6 +108,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/auth/repository/eventsourcing/handler/idp_config.go b/internal/auth/repository/eventsourcing/handler/idp_config.go index e6c21a6260..b070d89c8a 100644 --- a/internal/auth/repository/eventsourcing/handler/idp_config.go +++ b/internal/auth/repository/eventsourcing/handler/idp_config.go @@ -2,8 +2,10 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" @@ -11,25 +13,57 @@ import ( "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type IDPConfig struct { - handler -} - const ( idpConfigTable = "auth.idp_configs" ) +type IDPConfig struct { + handler + subscription *eventstore.Subscription +} + +func newIDPConfig(h handler) *IDPConfig { + idpConfig := &IDPConfig{ + handler: h, + } + + idpConfig.subscribe() + + return idpConfig +} + +func (i *IDPConfig) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} + func (i *IDPConfig) ViewModel() string { return idpConfigTable } +func (_ *IDPConfig) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (i *IDPConfig) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPConfigSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *IDPConfig) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPConfigSequence() + sequence, err := i.view.GetLatestIDPConfigSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -66,14 +100,14 @@ func (i *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, eve if err != nil { return err } - return i.view.DeleteIDPConfig(idp.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPConfig(idp.IDPConfigID, event) default: - return i.view.ProcessedIDPConfigSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPConfigSequence(event) } if err != nil { return err } - return i.view.PutIDPConfig(idp, idp.Sequence, event.CreationDate) + return i.view.PutIDPConfig(idp, event) } func (i *IDPConfig) OnError(event *models.Event, err error) error { diff --git a/internal/auth/repository/eventsourcing/handler/idp_providers.go b/internal/auth/repository/eventsourcing/handler/idp_providers.go index b92a20a3b4..8622fc57b1 100644 --- a/internal/auth/repository/eventsourcing/handler/idp_providers.go +++ b/internal/auth/repository/eventsourcing/handler/idp_providers.go @@ -2,42 +2,85 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" ) +const ( + idpProviderTable = "auth.idp_providers" +) + type IDPProvider struct { handler systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - idpProviderTable = "auth.idp_providers" -) +func newIDPProvider( + h handler, + defaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *IDPProvider { + idpProvider := &IDPProvider{ + handler: h, + systemDefaults: defaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + idpProvider.subscribe() + + return idpProvider +} + +func (i *IDPProvider) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *IDPProvider) ViewModel() string { return idpProviderTable } +func (_ *IDPProvider) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *IDPProvider) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestIDPProviderSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *IDPProvider) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestIDPProviderSequence() + sequence, err := i.view.GetLatestIDPProviderSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -64,7 +107,7 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event) case model.IDPConfigChanged, org_es_model.IDPConfigChanged: esConfig := new(iam_view_model.IDPConfigView) providerType := iam_model.IDPProviderTypeSystem @@ -88,16 +131,16 @@ func (i *IDPProvider) processIdpProvider(event *models.Event) (err error) { for _, provider := range providers { i.fillConfigData(provider, config) } - return i.view.PutIDPProviders(event.Sequence, event.CreationDate, providers...) + return i.view.PutIDPProviders(event, providers...) case org_es_model.LoginPolicyRemoved: - return i.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event) default: - return i.view.ProcessedIDPProviderSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedIDPProviderSequence(event) } if err != nil { return err } - return i.view.PutIDPProvider(provider, provider.Sequence, event.CreationDate) + return i.view.PutIDPProvider(provider, event) } func (i *IDPProvider) fillData(provider *iam_view_model.IDPProviderView) (err error) { diff --git a/internal/auth/repository/eventsourcing/handler/key.go b/internal/auth/repository/eventsourcing/handler/key.go index 48f224cc29..54703a2509 100644 --- a/internal/auth/repository/eventsourcing/handler/key.go +++ b/internal/auth/repository/eventsourcing/handler/key.go @@ -3,30 +3,62 @@ package handler import ( "time" - es_model "github.com/caos/zitadel/internal/key/repository/eventsourcing/model" - "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/key/repository/eventsourcing" + es_model "github.com/caos/zitadel/internal/key/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/key/repository/view/model" ) -type Key struct { - handler -} - const ( keyTable = "auth.keys" ) +type Key struct { + handler + subscription *eventstore.Subscription +} + +func newKey(handler handler) *Key { + h := &Key{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *Key) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (k *Key) ViewModel() string { return keyTable } +func (_ *Key) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.KeyPairAggregate} +} + +func (k *Key) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := k.view.GetLatestKeySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (k *Key) EventQuery() (*models.SearchQuery, error) { - sequence, err := k.view.GetLatestKeySequence() + sequence, err := k.view.GetLatestKeySequence("") if err != nil { return nil, err } @@ -41,11 +73,11 @@ func (k *Key) Reduce(event *models.Event) error { return err } if privateKey.Expiry.Before(time.Now()) && publicKey.Expiry.Before(time.Now()) { - return k.view.ProcessedKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedKeySequence(event) } - return k.view.PutKeys(privateKey, publicKey, event.Sequence, event.CreationDate) + return k.view.PutKeys(privateKey, publicKey, event) default: - return k.view.ProcessedKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedKeySequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/login_policy.go b/internal/auth/repository/eventsourcing/handler/login_policy.go index b92dd17a9e..35700d80c7 100644 --- a/internal/auth/repository/eventsourcing/handler/login_policy.go +++ b/internal/auth/repository/eventsourcing/handler/login_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type LoginPolicy struct { - handler -} - const ( loginPolicyTable = "auth.login_policies" ) +type LoginPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLoginPolicy(handler handler) *LoginPolicy { + h := &LoginPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *LoginPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *LoginPolicy) ViewModel() string { return loginPolicyTable } +func (_ *LoginPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *LoginPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestLoginPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *LoginPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestLoginPolicySequence() + sequence, err := p.view.GetLatestLoginPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -57,14 +91,14 @@ func (p *LoginPolicy) processLoginPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.LoginPolicyRemoved: - return p.view.DeleteLoginPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeleteLoginPolicy(event.AggregateID, event) default: - return p.view.ProcessedLoginPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedLoginPolicySequence(event) } if err != nil { return err } - return p.view.PutLoginPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutLoginPolicy(policy, event) } func (p *LoginPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/auth/repository/eventsourcing/handler/machine_keys.go b/internal/auth/repository/eventsourcing/handler/machine_keys.go index f9af7e86fb..0ca0532c26 100644 --- a/internal/auth/repository/eventsourcing/handler/machine_keys.go +++ b/internal/auth/repository/eventsourcing/handler/machine_keys.go @@ -5,73 +5,107 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type MachineKeys struct { - handler -} - const ( machineKeysTable = "auth.machine_keys" ) -func (d *MachineKeys) ViewModel() string { +type MachineKeys struct { + handler + subscription *eventstore.Subscription +} + +func newMachineKeys(handler handler) *MachineKeys { + h := &MachineKeys{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *MachineKeys) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + +func (k *MachineKeys) ViewModel() string { return machineKeysTable } -func (d *MachineKeys) EventQuery() (*models.SearchQuery, error) { - sequence, err := d.view.GetLatestMachineKeySequence() +func (_ *MachineKeys) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate} +} + +func (k *MachineKeys) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := k.view.GetLatestMachineKeySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (k *MachineKeys) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := k.view.GetLatestMachineKeySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate). + AggregateTypeFilter(k.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (d *MachineKeys) Reduce(event *models.Event) (err error) { +func (k *MachineKeys) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.UserAggregate: - err = d.processMachineKeys(event) + err = k.processMachineKeys(event) } return err } -func (d *MachineKeys) processMachineKeys(event *models.Event) (err error) { +func (k *MachineKeys) processMachineKeys(event *es_models.Event) (err error) { key := new(usr_model.MachineKeyView) switch event.Type { case model.MachineKeyAdded: err = key.AppendEvent(event) if key.ExpirationDate.Before(time.Now()) { - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedMachineKeySequence(event) } case model.MachineKeyRemoved: err = key.SetData(event) if err != nil { return err } - return d.view.DeleteMachineKey(key.ID, event.Sequence, event.CreationDate) + return k.view.DeleteMachineKey(key.ID, event) case model.UserRemoved: - return d.view.DeleteMachineKeysByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return k.view.DeleteMachineKeysByUserID(event.AggregateID, event) default: - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return k.view.ProcessedMachineKeySequence(event) } if err != nil { return err } - return d.view.PutMachineKey(key, key.Sequence, event.CreationDate) + return k.view.PutMachineKey(key, event) } -func (d *MachineKeys) OnError(event *models.Event, err error) error { +func (k *MachineKeys) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-S9fe", "id", event.AggregateID).WithError(err).Warn("something went wrong in machine key handler") - return spooler.HandleError(event, err, d.view.GetLatestMachineKeyFailedEvent, d.view.ProcessedMachineKeyFailedEvent, d.view.ProcessedMachineKeySequence, d.errorCountUntilSkip) + return spooler.HandleError(event, err, k.view.GetLatestMachineKeyFailedEvent, k.view.ProcessedMachineKeyFailedEvent, k.view.ProcessedMachineKeySequence, k.errorCountUntilSkip) } -func (d *MachineKeys) OnSuccess() error { - return spooler.HandleSuccess(d.view.UpdateMachineKeySpoolerRunTimestamp) +func (k *MachineKeys) OnSuccess() error { + return spooler.HandleSuccess(k.view.UpdateMachineKeySpoolerRunTimestamp) } diff --git a/internal/auth/repository/eventsourcing/handler/org.go b/internal/auth/repository/eventsourcing/handler/org.go index 909cfefc4a..ea87eed2f2 100644 --- a/internal/auth/repository/eventsourcing/handler/org.go +++ b/internal/auth/repository/eventsourcing/handler/org.go @@ -3,27 +3,62 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "auth.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (o *Org) subscribe() { + o.subscription = o.es.Subscribe(o.AggregateTypes()...) + go func() { + for event := range o.subscription.Events { + query.ReduceEvent(o, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (_ *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (o *Org) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } @@ -58,13 +93,13 @@ func (o *Org) Reduce(event *es_models.Event) (err error) { } org.Domain = domain.Domain default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } if err != nil { return err } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/auth/repository/eventsourcing/handler/org_iam_policy.go b/internal/auth/repository/eventsourcing/handler/org_iam_policy.go index ac28b5c7c4..9d9d230a96 100644 --- a/internal/auth/repository/eventsourcing/handler/org_iam_policy.go +++ b/internal/auth/repository/eventsourcing/handler/org_iam_policy.go @@ -2,68 +2,101 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type OrgIAMPolicy struct { - handler -} - const ( orgIAMPolicyTable = "auth.org_iam_policies" ) +type OrgIAMPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newOrgIAMPolicy(handler handler) *OrgIAMPolicy { + h := &OrgIAMPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *OrgIAMPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *OrgIAMPolicy) ViewModel() string { return orgIAMPolicyTable } -func (p *OrgIAMPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestOrgIAMPolicySequence() +func (_ *OrgIAMPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{org_es_model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *OrgIAMPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *OrgIAMPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *OrgIAMPolicy) Reduce(event *models.Event) (err error) { +func (p *OrgIAMPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { - case model.OrgAggregate, iam_es_model.IAMAggregate: + case org_es_model.OrgAggregate, iam_es_model.IAMAggregate: err = p.processOrgIAMPolicy(event) } return err } -func (p *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { +func (p *OrgIAMPolicy) processOrgIAMPolicy(event *es_models.Event) (err error) { policy := new(iam_model.OrgIAMPolicyView) switch event.Type { - case iam_es_model.OrgIAMPolicyAdded, model.OrgIAMPolicyAdded: + case iam_es_model.OrgIAMPolicyAdded, org_es_model.OrgIAMPolicyAdded: err = policy.AppendEvent(event) - case iam_es_model.OrgIAMPolicyChanged, model.OrgIAMPolicyChanged: + case iam_es_model.OrgIAMPolicyChanged, org_es_model.OrgIAMPolicyChanged: policy, err = p.view.OrgIAMPolicyByAggregateID(event.AggregateID) if err != nil { return err } err = policy.AppendEvent(event) - case model.OrgIAMPolicyRemoved: - return p.view.DeleteOrgIAMPolicy(event.AggregateID, event.Sequence, event.CreationDate) + case org_es_model.OrgIAMPolicyRemoved: + return p.view.DeleteOrgIAMPolicy(event.AggregateID, event) default: - return p.view.ProcessedOrgIAMPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedOrgIAMPolicySequence(event) } if err != nil { return err } - return p.view.PutOrgIAMPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutOrgIAMPolicy(policy, event) } -func (p *OrgIAMPolicy) OnError(event *models.Event, err error) error { +func (p *OrgIAMPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-3Gj8s", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgIAM policy handler") return spooler.HandleError(event, err, p.view.GetLatestOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicyFailedEvent, p.view.ProcessedOrgIAMPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go b/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go index d3656a164a..65a86676a8 100644 --- a/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go +++ b/internal/auth/repository/eventsourcing/handler/password_complexity_policy.go @@ -2,68 +2,101 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordComplexityPolicy struct { - handler -} - const ( passwordComplexityPolicyTable = "auth.password_complexity_policies" ) +type PasswordComplexityPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordComplexityPolicy(handler handler) *PasswordComplexityPolicy { + h := &PasswordComplexityPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (p *PasswordComplexityPolicy) subscribe() { + p.subscription = p.es.Subscribe(p.AggregateTypes()...) + go func() { + for event := range p.subscription.Events { + query.ReduceEvent(p, event) + } + }() +} + func (p *PasswordComplexityPolicy) ViewModel() string { return passwordComplexityPolicyTable } -func (p *PasswordComplexityPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordComplexityPolicySequence() +func (_ *PasswordComplexityPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{org_es_model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordComplexityPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *PasswordComplexityPolicy) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (p *PasswordComplexityPolicy) Reduce(event *models.Event) (err error) { +func (p *PasswordComplexityPolicy) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { - case model.OrgAggregate, iam_es_model.IAMAggregate: + case org_es_model.OrgAggregate, iam_es_model.IAMAggregate: err = p.processPasswordComplexityPolicy(event) } return err } -func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *models.Event) (err error) { +func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *es_models.Event) (err error) { policy := new(iam_model.PasswordComplexityPolicyView) switch event.Type { - case iam_es_model.PasswordComplexityPolicyAdded, model.PasswordComplexityPolicyAdded: + case iam_es_model.PasswordComplexityPolicyAdded, org_es_model.PasswordComplexityPolicyAdded: err = policy.AppendEvent(event) - case iam_es_model.PasswordComplexityPolicyChanged, model.PasswordComplexityPolicyChanged: + case iam_es_model.PasswordComplexityPolicyChanged, org_es_model.PasswordComplexityPolicyChanged: policy, err = p.view.PasswordComplexityPolicyByAggregateID(event.AggregateID) if err != nil { return err } err = policy.AppendEvent(event) - case model.PasswordComplexityPolicyRemoved: - return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event.Sequence, event.CreationDate) + case org_es_model.PasswordComplexityPolicyRemoved: + return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordComplexityPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordComplexityPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordComplexityPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordComplexityPolicy(policy, event) } -func (p *PasswordComplexityPolicy) OnError(event *models.Event, err error) error { +func (p *PasswordComplexityPolicy) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-4Djo9", "id", event.AggregateID).WithError(err).Warn("something went wrong in passwordComplexity policy handler") return spooler.HandleError(event, err, p.view.GetLatestPasswordComplexityPolicyFailedEvent, p.view.ProcessedPasswordComplexityPolicyFailedEvent, p.view.ProcessedPasswordComplexityPolicySequence, p.errorCountUntilSkip) } diff --git a/internal/auth/repository/eventsourcing/handler/project_role.go b/internal/auth/repository/eventsourcing/handler/project_role.go index c469019b9f..5ee8722bdc 100644 --- a/internal/auth/repository/eventsourcing/handler/project_role.go +++ b/internal/auth/repository/eventsourcing/handler/project_role.go @@ -2,42 +2,80 @@ package handler import ( "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" - "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" - es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" + proj_events "github.com/caos/zitadel/internal/project/repository/eventsourcing" + "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type ProjectRole struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( projectRoleTable = "auth.project_roles" ) +type ProjectRole struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newProjectRole( + handler handler, + projectEvents *proj_events.ProjectEventstore, +) *ProjectRole { + h := &ProjectRole{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (k *ProjectRole) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (p *ProjectRole) ViewModel() string { return projectRoleTable } -func (p *ProjectRole) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectRoleSequence() +func (_ *ProjectRole) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.ProjectAggregate} +} + +func (p *ProjectRole) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectRoleSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *ProjectRole) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestProjectRoleSequence("") if err != nil { return nil, err } - return eventsourcing.ProjectQuery(sequence.CurrentSequence), nil + return proj_events.ProjectQuery(sequence.CurrentSequence), nil } -func (p *ProjectRole) Reduce(event *models.Event) (err error) { +func (p *ProjectRole) Reduce(event *es_models.Event) (err error) { role := new(view_model.ProjectRoleView) switch event.Type { - case es_model.ProjectRoleAdded: + case model.ProjectRoleAdded: err = role.AppendEvent(event) - case es_model.ProjectRoleChanged: + case model.ProjectRoleChanged: err = role.SetData(event) if err != nil { return err @@ -47,24 +85,27 @@ func (p *ProjectRole) Reduce(event *models.Event) (err error) { return err } err = role.AppendEvent(event) - case es_model.ProjectRoleRemoved: + case model.ProjectRoleRemoved: err = role.SetData(event) if err != nil { return err } - return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event.Sequence, event.CreationDate) - case es_model.ProjectRemoved: - return p.view.DeleteProjectRolesByProjectID(event.AggregateID) + return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event) + case model.ProjectRemoved: + err := p.view.DeleteProjectRolesByProjectID(event.AggregateID) + if err == nil { + return p.view.ProcessedProjectRoleSequence(event) + } default: - return p.view.ProcessedProjectRoleSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectRoleSequence(event) } if err != nil { return err } - return p.view.PutProjectRole(role, event.CreationDate) + return p.view.PutProjectRole(role, event) } -func (p *ProjectRole) OnError(event *models.Event, err error) error { +func (p *ProjectRole) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-lso9w", "id", event.AggregateID).WithError(err).Warn("something went wrong in project role handler") return spooler.HandleError(event, err, p.view.GetLatestProjectRoleFailedEvent, p.view.ProcessedProjectRoleFailedEvent, p.view.ProcessedProjectRoleSequence, p.errorCountUntilSkip) } diff --git a/internal/auth/repository/eventsourcing/handler/token.go b/internal/auth/repository/eventsourcing/handler/token.go index 2c056cc064..b98e811bda 100644 --- a/internal/auth/repository/eventsourcing/handler/token.go +++ b/internal/auth/repository/eventsourcing/handler/token.go @@ -6,8 +6,10 @@ import ( "github.com/caos/logging" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" project_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" @@ -15,21 +17,57 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type Token struct { - handler - ProjectEvents *proj_event.ProjectEventstore -} - const ( tokenTable = "auth.tokens" ) +type Token struct { + handler + ProjectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newToken( + handler handler, + projectEvents *proj_event.ProjectEventstore, +) *Token { + h := &Token{ + handler: handler, + ProjectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (t *Token) subscribe() { + t.subscription = t.es.Subscribe(t.AggregateTypes()...) + go func() { + for event := range t.subscription.Events { + query.ReduceEvent(t, event) + } + }() +} + func (t *Token) ViewModel() string { return tokenTable } +func (_ *Token) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{user_es_model.UserAggregate, project_es_model.ProjectAggregate} +} + +func (p *Token) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestTokenSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (t *Token) EventQuery() (*models.SearchQuery, error) { - sequence, err := t.view.GetLatestTokenSequence() + sequence, err := t.view.GetLatestTokenSequence("") if err != nil { return nil, err } @@ -49,7 +87,7 @@ func (t *Token) Reduce(event *models.Event) (err error) { if err != nil { return err } - return t.view.PutToken(token, event.CreationDate) + return t.view.PutToken(token, event) case user_es_model.UserProfileChanged, user_es_model.HumanProfileChanged: user := new(view_model.UserView) @@ -61,25 +99,25 @@ func (t *Token) Reduce(event *models.Event) (err error) { for _, token := range tokens { token.PreferredLanguage = user.PreferredLanguage } - return t.view.PutTokens(tokens, event.Sequence, event.CreationDate) + return t.view.PutTokens(tokens, event) case user_es_model.SignedOut, user_es_model.HumanSignedOut: id, err := agentIDFromSession(event) if err != nil { return err } - return t.view.DeleteSessionTokens(id, event.AggregateID, event.Sequence, event.CreationDate) + return t.view.DeleteSessionTokens(id, event.AggregateID, event) case user_es_model.UserLocked, user_es_model.UserDeactivated, user_es_model.UserRemoved: - return t.view.DeleteUserTokens(event.AggregateID, event.Sequence, event.CreationDate) + return t.view.DeleteUserTokens(event.AggregateID, event) case project_es_model.ApplicationDeactivated, project_es_model.ApplicationRemoved: application, err := applicationFromSession(event) if err != nil { return err } - return t.view.DeleteApplicationTokens(event.Sequence, event.CreationDate, application.AppID) + return t.view.DeleteApplicationTokens(event, application.AppID) case project_es_model.ProjectDeactivated, project_es_model.ProjectRemoved: project, err := t.ProjectEvents.ProjectByID(context.Background(), event.AggregateID) @@ -90,9 +128,9 @@ func (t *Token) Reduce(event *models.Event) (err error) { for _, app := range project.Applications { applicationsIDs = append(applicationsIDs, app.AppID) } - return t.view.DeleteApplicationTokens(event.Sequence, event.CreationDate, applicationsIDs...) + return t.view.DeleteApplicationTokens(event, applicationsIDs...) default: - return t.view.ProcessedTokenSequence(event.Sequence, event.CreationDate) + return t.view.ProcessedTokenSequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/user.go b/internal/auth/repository/eventsourcing/handler/user.go index 8b1ceb091c..f960ab720d 100644 --- a/internal/auth/repository/eventsourcing/handler/user.go +++ b/internal/auth/repository/eventsourcing/handler/user.go @@ -2,45 +2,83 @@ package handler import ( "context" - iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type User struct { - handler - eventstore eventstore.Eventstore - orgEvents *org_events.OrgEventstore - iamEvents *iam_es.IAMEventstore - iamID string -} - const ( userTable = "auth.users" ) +type User struct { + handler + orgEvents *org_events.OrgEventstore + iamEvents *iam_es.IAMEventstore + iamID string + subscription *eventstore.Subscription +} + +func newUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + iamID string, +) *User { + h := &User{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *User) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (u *User) ViewModel() string { return userTable } +func (_ *User) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (u *User) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *User) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSequence() + sequence, err := u.view.GetLatestUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -120,14 +158,14 @@ func (u *User) ProcessUser(event *models.Event) (err error) { } err = u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUser(event.AggregateID, event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } if err != nil { return err } - return u.view.PutUser(user, user.Sequence, event.CreationDate) + return u.view.PutUser(user, event) } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { @@ -158,7 +196,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } } @@ -181,7 +219,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.SetLoginNames(policy, org.Domains) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { @@ -206,7 +244,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) } - return u.view.PutUsers(users, 0, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) OnError(event *models.Event, err error) error { diff --git a/internal/auth/repository/eventsourcing/handler/user_external_idps.go b/internal/auth/repository/eventsourcing/handler/user_external_idps.go index 826d79170c..8dcaebe3cb 100644 --- a/internal/auth/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/auth/repository/eventsourcing/handler/user_external_idps.go @@ -2,11 +2,14 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" @@ -18,28 +21,68 @@ import ( usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + externalIDPTable = "auth.user_external_idps" +) + type ExternalIDP struct { handler systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - externalIDPTable = "auth.user_external_idps" -) +func newExternalIDP( + handler handler, + defaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *ExternalIDP { + h := &ExternalIDP{ + handler: handler, + systemDefaults: defaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (i *ExternalIDP) subscribe() { + i.subscription = i.es.Subscribe(i.AggregateTypes()...) + go func() { + for event := range i.subscription.Events { + query.ReduceEvent(i, event) + } + }() +} func (i *ExternalIDP) ViewModel() string { return externalIDPTable } +func (_ *ExternalIDP) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *ExternalIDP) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestExternalIDPSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (i *ExternalIDP) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestExternalIDPSequence() + sequence, err := i.view.GetLatestExternalIDPSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -67,16 +110,16 @@ func (i *ExternalIDP) processUser(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event) case model.UserRemoved: - return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } if err != nil { return err } - return i.view.PutExternalIDP(externalIDP, externalIDP.Sequence, event.CreationDate) + return i.view.PutExternalIDP(externalIDP, event) } func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { @@ -104,9 +147,9 @@ func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { for _, provider := range exterinalIDPs { i.fillConfigData(provider, config) } - return i.view.PutExternalIDPs(event.Sequence, event.CreationDate, exterinalIDPs...) + return i.view.PutExternalIDPs(event, exterinalIDPs...) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } return nil } diff --git a/internal/auth/repository/eventsourcing/handler/user_grant.go b/internal/auth/repository/eventsourcing/handler/user_grant.go index 8aa95311db..4244d0ac34 100644 --- a/internal/auth/repository/eventsourcing/handler/user_grant.go +++ b/internal/auth/repository/eventsourcing/handler/user_grant.go @@ -11,6 +11,7 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" @@ -28,25 +29,68 @@ import ( "github.com/caos/zitadel/internal/v2/domain" ) +const ( + userGrantTable = "auth.user_grants" +) + type UserGrant struct { handler - eventstore eventstore.Eventstore projectEvents *proj_event.ProjectEventstore userEvents *usr_events.UserEventstore orgEvents *org_events.OrgEventstore iamEvents *iam_events.IAMEventstore iamID string iamProjectID string + subscription *eventstore.Subscription } -const ( - userGrantTable = "auth.user_grants" -) +func newUserGrant( + handler handler, + projectEvents *proj_event.ProjectEventstore, + userEvents *usr_events.UserEventstore, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_events.IAMEventstore, + iamID string, +) *UserGrant { + h := &UserGrant{ + handler: handler, + projectEvents: projectEvents, + userEvents: userEvents, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *UserGrant) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} func (u *UserGrant) ViewModel() string { return userGrantTable } +func (_ *UserGrant) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{grant_es_model.UserGrantAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate} +} + +func (u *UserGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { if u.iamProjectID == "" { err := u.setIamProjectID() @@ -54,12 +98,12 @@ func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { return nil, err } } - sequence, err := u.view.GetLatestUserGrantSequence() + sequence, err := u.view.GetLatestUserGrantSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(grant_es_model.UserGrantAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -98,14 +142,14 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) { } err = grant.AppendEvent(event) case grant_es_model.UserGrantRemoved, grant_es_model.UserGrantCascadeRemoved: - return u.view.DeleteUserGrant(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(event.AggregateID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if err != nil { return err } - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) } func (u *UserGrant) processUser(event *models.Event) (err error) { @@ -120,7 +164,7 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { return err } if len(grants) == 0 { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } user, err := u.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -129,9 +173,9 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { for _, grant := range grants { u.fillUserData(grant, user) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -149,7 +193,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { for _, grant := range grants { u.fillProjectData(grant, project) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectMemberChanged, proj_es_model.ProjectMemberRemoved: member := new(proj_es_model.ProjectMember) member.SetData(event) @@ -159,7 +203,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "PROJECT_GRANT", member.GrantID, member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -170,7 +214,7 @@ func (u *UserGrant) processOrg(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "ORG", "", member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -208,16 +252,16 @@ func (u *UserGrant) processIAMMember(event *models.Event, rolePrefix string, suf } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case iam_es_model.IAMMemberRemoved: member.SetData(event) grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID) if err != nil { return err } - return u.view.DeleteUserGrant(grant.ID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(grant.ID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -253,7 +297,7 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case org_es_model.OrgMemberRemoved, proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectGrantMemberRemoved: @@ -263,18 +307,18 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st return err } if errors.IsNotFound(err) { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if roleSuffix != "" { roleKeys = suffixRoles(roleSuffix, roleKeys) } if grant.RoleKeys == nil { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } grant.RoleKeys = mergeExistingRoles(rolePrefix, roleSuffix, grant.RoleKeys, nil) - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/user_membership.go b/internal/auth/repository/eventsourcing/handler/user_membership.go index 7b231a78b5..e1a41eccf5 100644 --- a/internal/auth/repository/eventsourcing/handler/user_membership.go +++ b/internal/auth/repository/eventsourcing/handler/user_membership.go @@ -2,52 +2,90 @@ package handler import ( "context" - "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore/models" - es_models "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" - org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/model" + "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + userMembershipTable = "auth.user_memberships" +) + type UserMembership struct { handler orgEvents *org_event.OrgEventstore projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription } -const ( - userMembershipTable = "auth.user_memberships" -) +func newUserMembership( + handler handler, + orgEvents *org_event.OrgEventstore, + projectEvents *proj_event.ProjectEventstore, +) *UserMembership { + h := &UserMembership{ + handler: handler, + orgEvents: orgEvents, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (m *UserMembership) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (m *UserMembership) ViewModel() string { return userMembershipTable } +func (_ *UserMembership) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate} +} + +func (m *UserMembership) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestUserMembershipSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *UserMembership) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestUserMembershipSequence() + sequence, err := m.view.GetLatestUserMembershipSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } func (m *UserMembership) Reduce(event *models.Event) (err error) { switch event.AggregateType { case iam_es_model.IAMAggregate: - err = m.processIam(event) + err = m.processIAM(event) case org_es_model.OrgAggregate: err = m.processOrg(event) case proj_es_model.ProjectAggregate: @@ -58,7 +96,7 @@ func (m *UserMembership) Reduce(event *models.Event) (err error) { return err } -func (m *UserMembership) processIam(event *models.Event) (err error) { +func (m *UserMembership) processIAM(event *models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { @@ -74,14 +112,14 @@ func (m *UserMembership) processIam(event *models.Event) (err error) { } err = member.AppendEvent(event) case iam_es_model.IAMMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillIamDisplayName(member *usr_es_model.UserMembershipView) { @@ -105,16 +143,16 @@ func (m *UserMembership) processOrg(event *models.Event) (err error) { } err = member.AppendEvent(event) case org_es_model.OrgMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event) case org_es_model.OrgChanged: return m.updateOrgName(event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillOrgName(member *usr_es_model.UserMembershipView) (err error) { @@ -145,7 +183,7 @@ func (m *UserMembership) updateOrgName(event *models.Event) error { membership.DisplayName = org.Name } } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } func (m *UserMembership) processProject(event *models.Event) (err error) { @@ -168,7 +206,7 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event) case proj_es_model.ProjectGrantMemberChanged: member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant) if err != nil { @@ -176,20 +214,20 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectGrantMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event) case proj_es_model.ProjectChanged: return m.updateProjectDisplayName(event) case proj_es_model.ProjectRemoved: - return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event) case proj_es_model.ProjectGrantRemoved: - return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembershipView) (err error) { @@ -214,15 +252,15 @@ func (m *UserMembership) updateProjectDisplayName(event *models.Event) error { for _, membership := range memberships { membership.DisplayName = project.Name } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } func (m *UserMembership) processUser(event *models.Event) (err error) { switch event.Type { case model.UserRemoved: - return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } } diff --git a/internal/auth/repository/eventsourcing/handler/user_session.go b/internal/auth/repository/eventsourcing/handler/user_session.go index b55aacfd63..7ef182f6ae 100644 --- a/internal/auth/repository/eventsourcing/handler/user_session.go +++ b/internal/auth/repository/eventsourcing/handler/user_session.go @@ -1,34 +1,70 @@ package handler import ( + "github.com/caos/logging" req_model "github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/errors" - es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - - "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/user/repository/eventsourcing" user_events "github.com/caos/zitadel/internal/user/repository/eventsourcing" + es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type UserSession struct { - handler - userEvents *user_events.UserEventstore -} - const ( userSessionTable = "auth.user_sessions" ) +type UserSession struct { + handler + userEvents *user_events.UserEventstore + subscription *eventstore.Subscription +} + +func newUserSession( + handler handler, + userEvents *user_events.UserEventstore, +) *UserSession { + h := &UserSession{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (k *UserSession) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (u *UserSession) ViewModel() string { return userSessionTable } +func (_ *UserSession) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate} +} + +func (u *UserSession) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSessionSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *UserSession) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSessionSequence() + sequence, err := u.view.GetLatestUserSessionSequence("") if err != nil { return nil, err } @@ -90,7 +126,7 @@ func (u *UserSession) Reduce(event *models.Event) (err error) { return err } if len(sessions) == 0 { - return u.view.ProcessedUserSessionSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSessionSequence(event) } for _, session := range sessions { if err := session.AppendEvent(event); err != nil { @@ -100,11 +136,11 @@ func (u *UserSession) Reduce(event *models.Event) (err error) { return err } } - return u.view.PutUserSessions(sessions, event.Sequence, event.CreationDate) + return u.view.PutUserSessions(sessions, event) case es_model.UserRemoved: - return u.view.DeleteUserSessions(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUserSessions(event.AggregateID, event) default: - return u.view.ProcessedUserSessionSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSessionSequence(event) } } @@ -124,7 +160,7 @@ func (u *UserSession) updateSession(session *view_model.UserSessionView, event * if err := u.fillUserInfo(session, event.AggregateID); err != nil { return err } - return u.view.PutUserSession(session, event.CreationDate) + return u.view.PutUserSession(session, event) } func (u *UserSession) fillUserInfo(session *view_model.UserSessionView, id string) error { diff --git a/internal/auth/repository/eventsourcing/spooler/lock.go b/internal/auth/repository/eventsourcing/spooler/lock.go index 1a7ea2990e..5b69f3ae0c 100644 --- a/internal/auth/repository/eventsourcing/spooler/lock.go +++ b/internal/auth/repository/eventsourcing/spooler/lock.go @@ -2,8 +2,9 @@ package spooler import ( "database/sql" - es_locker "github.com/caos/zitadel/internal/eventstore/locker" "time" + + es_locker "github.com/caos/zitadel/internal/eventstore/locker" ) const ( diff --git a/internal/auth/repository/eventsourcing/view/application.go b/internal/auth/repository/eventsourcing/view/application.go index 556d31d251..5a7f2c6f1b 100644 --- a/internal/auth/repository/eventsourcing/view/application.go +++ b/internal/auth/repository/eventsourcing/view/application.go @@ -2,13 +2,14 @@ package view import ( "context" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" global_model "github.com/caos/zitadel/internal/model" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -27,28 +28,28 @@ func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) return view.SearchApplications(v.Db, applicationTable, request) } -func (v *View) PutApplication(app *model.ApplicationView, eventTimestamp time.Time) error { +func (v *View) PutApplication(app *model.ApplicationView, event *models.Event) error { err := view.PutApplication(v.Db, applicationTable, app) if err != nil { return err } - return v.ProcessedApplicationSequence(app.Sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) PutApplications(apps []*model.ApplicationView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Event) error { err := view.PutApplications(v.Db, applicationTable, apps...) if err != nil { return err } - return v.ProcessedApplicationSequence(sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) DeleteApplication(appID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedApplicationSequence(eventSequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } func (v *View) DeleteApplicationsByProjectID(projectID string) error { @@ -56,11 +57,11 @@ func (v *View) DeleteApplicationsByProjectID(projectID string) error { } func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(applicationTable) + return v.latestSequence(applicationTable, "") } -func (v *View) ProcessedApplicationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(applicationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedApplicationSequence(event *models.Event) error { + return v.saveCurrentSequence(applicationTable, event) } func (v *View) UpdateApplicationSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/external_idps.go b/internal/auth/repository/eventsourcing/view/external_idps.go index 1381611037..2928421cdc 100644 --- a/internal/auth/repository/eventsourcing/view/external_idps.go +++ b/internal/auth/repository/eventsourcing/view/external_idps.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -33,44 +33,44 @@ func (v *View) SearchExternalIDPs(request *usr_model.ExternalIDPSearchRequest) ( return view.SearchExternalIDPs(v.Db, externalIDPTable, request) } -func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, event *models.Event) error { err := view.PutExternalIDP(v.Db, externalIDPTable, externalIDP) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) PutExternalIDPs(sequence uint64, eventTimestamp time.Time, externalIDPs ...*model.ExternalIDPView) error { +func (v *View) PutExternalIDPs(event *models.Event, externalIDPs ...*model.ExternalIDPView) error { err := view.PutExternalIDPs(v.Db, externalIDPTable, externalIDPs...) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, event *models.Event) error { err := view.DeleteExternalIDP(v.Db, externalIDPTable, externalUserID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDPsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDPsByUserID(userID string, event *models.Event) error { err := view.DeleteExternalIDPsByUserID(v.Db, externalIDPTable, userID) if err != nil { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) GetLatestExternalIDPSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(externalIDPTable) +func (v *View) GetLatestExternalIDPSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(externalIDPTable, aggregateType) } -func (v *View) ProcessedExternalIDPSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(externalIDPTable, eventSequence, eventTimestamp) +func (v *View) ProcessedExternalIDPSequence(event *models.Event) error { + return v.saveCurrentSequence(externalIDPTable, event) } func (v *View) UpdateExternalIDPSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/idp_configs.go b/internal/auth/repository/eventsourcing/view/idp_configs.go index df591a7a00..65a932b2d1 100644 --- a/internal/auth/repository/eventsourcing/view/idp_configs.go +++ b/internal/auth/repository/eventsourcing/view/idp_configs.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -25,28 +25,28 @@ func (v *View) SearchIDPConfigs(request *iam_model.IDPConfigSearchRequest) ([]*i return view.SearchIDPs(v.Db, idpConfigTable, request) } -func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, event *models.Event) error { err := view.PutIDP(v.Db, idpConfigTable, idp) if err != nil { return err } - return v.ProcessedIDPConfigSequence(sequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) DeleteIDPConfig(idpID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPConfig(idpID string, event *models.Event) error { err := view.DeleteIDP(v.Db, idpConfigTable, idpID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPConfigSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) GetLatestIDPConfigSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpConfigTable) +func (v *View) GetLatestIDPConfigSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpConfigTable, aggregateType) } -func (v *View) ProcessedIDPConfigSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpConfigTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPConfigSequence(event *models.Event) error { + return v.saveCurrentSequence(idpConfigTable, event) } func (v *View) UpdateIDPConfigSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/idp_providers.go b/internal/auth/repository/eventsourcing/view/idp_providers.go index cf81e63745..afd25c15f8 100644 --- a/internal/auth/repository/eventsourcing/view/idp_providers.go +++ b/internal/auth/repository/eventsourcing/view/idp_providers.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -29,44 +29,44 @@ func (v *View) SearchIDPProviders(request *iam_model.IDPProviderSearchRequest) ( return view.SearchIDPProviders(v.Db, idpProviderTable, request) } -func (v *View) PutIDPProvider(provider *model.IDPProviderView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPProvider(provider *model.IDPProviderView, event *models.Event) error { err := view.PutIDPProvider(v.Db, idpProviderTable, provider) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) PutIDPProviders(sequence uint64, eventTimestamp time.Time, providers ...*model.IDPProviderView) error { +func (v *View) PutIDPProviders(event *models.Event, providers ...*model.IDPProviderView) error { err := view.PutIDPProviders(v.Db, idpProviderTable, providers...) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, event *models.Event) error { err := view.DeleteIDPProvider(v.Db, idpProviderTable, aggregateID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteIDPProvidersByAggregateID(v.Db, idpProviderTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) GetLatestIDPProviderSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpProviderTable) +func (v *View) GetLatestIDPProviderSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpProviderTable, aggregateType) } -func (v *View) ProcessedIDPProviderSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpProviderTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPProviderSequence(event *models.Event) error { + return v.saveCurrentSequence(idpProviderTable, event) } func (v *View) UpdateIDPProviderSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/key.go b/internal/auth/repository/eventsourcing/view/key.go index c0c0f7d357..f8fca89445 100644 --- a/internal/auth/repository/eventsourcing/view/key.go +++ b/internal/auth/repository/eventsourcing/view/key.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/key/repository/view" "github.com/caos/zitadel/internal/key/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,36 +33,36 @@ func (v *View) GetActiveKeySet() ([]*key_model.PublicKey, error) { return key_model.PublicKeysFromKeyView(model.KeyViewsToModel(keys), v.keyAlgorithm) } -func (v *View) PutKeys(privateKey, publicKey *model.KeyView, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) PutKeys(privateKey, publicKey *model.KeyView, event *models.Event) error { err := view.PutKeys(v.Db, keyTable, privateKey, publicKey) if err != nil { return err } - return v.ProcessedKeySequence(eventSequence, eventTimestamp) + return v.ProcessedKeySequence(event) } -func (v *View) DeleteKey(keyID string, private bool, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteKey(keyID string, private bool, event *models.Event) error { err := view.DeleteKey(v.Db, keyTable, keyID, private) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedKeySequence(eventSequence, eventTimestamp) + return v.ProcessedKeySequence(event) } -func (v *View) DeleteKeyPair(keyID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteKeyPair(keyID string, event *models.Event) error { err := view.DeleteKeyPair(v.Db, keyTable, keyID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedKeySequence(eventSequence, eventTimestamp) + return v.ProcessedKeySequence(event) } -func (v *View) GetLatestKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(keyTable) +func (v *View) GetLatestKeySequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(keyTable, aggregateType) } -func (v *View) ProcessedKeySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(keyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedKeySequence(event *models.Event) error { + return v.saveCurrentSequence(keyTable, event) } func (v *View) UpdateKeySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/login_policies.go b/internal/auth/repository/eventsourcing/view/login_policies.go index 2da8862129..2687cd8b6d 100644 --- a/internal/auth/repository/eventsourcing/view/login_policies.go +++ b/internal/auth/repository/eventsourcing/view/login_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LoginPolicyByAggregateID(aggregateID string) (*model.LoginPolicyV return view.GetLoginPolicyByAggregateID(v.Db, loginPolicyTable, aggregateID) } -func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, event *models.Event) error { err := view.PutLoginPolicy(v.Db, loginPolicyTable, policy) if err != nil { return err } - return v.ProcessedLoginPolicySequence(sequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) DeleteLoginPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLoginPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLoginPolicy(v.Db, loginPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLoginPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) GetLatestLoginPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(loginPolicyTable) +func (v *View) GetLatestLoginPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(loginPolicyTable, aggregateType) } -func (v *View) ProcessedLoginPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(loginPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLoginPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(loginPolicyTable, event) } func (v *View) UpdateLoginPolicySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/machine_keys.go b/internal/auth/repository/eventsourcing/view/machine_keys.go index ac1a442566..b5412d7240 100644 --- a/internal/auth/repository/eventsourcing/view/machine_keys.go +++ b/internal/auth/repository/eventsourcing/view/machine_keys.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,39 +29,36 @@ func (v *View) SearchMachineKeys(request *usr_model.MachineKeySearchRequest) ([] return view.SearchMachineKeys(v.Db, machineKeyTable, request) } -func (v *View) PutMachineKey(key *model.MachineKeyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutMachineKey(key *model.MachineKeyView, event *models.Event) error { err := view.PutMachineKey(v.Db, machineKeyTable, key) if err != nil { return err } - if sequence != 0 { - return v.ProcessedMachineKeySequence(sequence, eventTimestamp) - } - return nil + return v.ProcessedMachineKeySequence(event) } -func (v *View) DeleteMachineKey(keyID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) DeleteMachineKeysByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) GetLatestMachineKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(machineKeyTable) +func (v *View) GetLatestMachineKeySequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(machineKeyTable, aggregateType) } -func (v *View) ProcessedMachineKeySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(machineKeyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedMachineKeySequence(event *models.Event) error { + return v.saveCurrentSequence(machineKeyTable, event) } func (v *View) UpdateMachineKeySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/org.go b/internal/auth/repository/eventsourcing/view/org.go index 9f489f5c30..1b9a7c1b4c 100644 --- a/internal/auth/repository/eventsourcing/view/org.go +++ b/internal/auth/repository/eventsourcing/view/org.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/org/model" org_view "github.com/caos/zitadel/internal/org/repository/view" org_model "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,12 +24,12 @@ func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, ui return org_view.SearchOrgs(v.Db, orgTable, req) } -func (v *View) PutOrg(org *org_model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *org_model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -44,10 +44,10 @@ func (v *View) UpdateOrgSpoolerRunTimestamp() error { return v.updateSpoolerRunSequence(orgTable) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } diff --git a/internal/auth/repository/eventsourcing/view/org_iam_policy.go b/internal/auth/repository/eventsourcing/view/org_iam_policy.go index a9ee5a295f..a05592dae3 100644 --- a/internal/auth/repository/eventsourcing/view/org_iam_policy.go +++ b/internal/auth/repository/eventsourcing/view/org_iam_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) OrgIAMPolicyByAggregateID(aggregateID string) (*model.OrgIAMPolic return view.GetOrgIAMPolicyByAggregateID(v.Db, orgIAMPolicyTable, aggregateID) } -func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, event *models.Event) error { err := view.PutOrgIAMPolicy(v.Db, orgIAMPolicyTable, policy) if err != nil { return err } - return v.ProcessedOrgIAMPolicySequence(sequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) DeleteOrgIAMPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgIAMPolicy(aggregateID string, event *models.Event) error { err := view.DeleteOrgIAMPolicy(v.Db, orgIAMPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedOrgIAMPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) GetLatestOrgIAMPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(orgIAMPolicyTable) +func (v *View) GetLatestOrgIAMPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(orgIAMPolicyTable, aggregateType) } -func (v *View) ProcessedOrgIAMPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgIAMPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgIAMPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(orgIAMPolicyTable, event) } func (v *View) UpdateOrgIAMPolicySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/password_complexity_policy.go b/internal/auth/repository/eventsourcing/view/password_complexity_policy.go index 8259c89da0..43ce74f8c1 100644 --- a/internal/auth/repository/eventsourcing/view/password_complexity_policy.go +++ b/internal/auth/repository/eventsourcing/view/password_complexity_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordComplexityPolicyByAggregateID(aggregateID string) (*model return view.GetPasswordComplexityPolicyByAggregateID(v.Db, passwordComplexityPolicyTable, aggregateID) } -func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, event *models.Event) error { err := view.PutPasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordComplexityPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) DeletePasswordComplexityPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordComplexityPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordComplexityPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) GetLatestPasswordComplexityPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordComplexityPolicyTable) +func (v *View) GetLatestPasswordComplexityPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordComplexityPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordComplexityPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordComplexityPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordComplexityPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordComplexityPolicyTable, event) } func (v *View) UpdatePasswordComplexityPolicySpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/project_role.go b/internal/auth/repository/eventsourcing/view/project_role.go index 66c287ffa1..c5a99bfe4a 100644 --- a/internal/auth/repository/eventsourcing/view/project_role.go +++ b/internal/auth/repository/eventsourcing/view/project_role.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,32 +33,32 @@ func (v *View) SearchProjectRoles(request *proj_model.ProjectRoleSearchRequest) return view.SearchProjectRoles(v.Db, projectRoleTable, request) } -func (v *View) PutProjectRole(project *model.ProjectRoleView, eventTimestamp time.Time) error { - err := view.PutProjectRole(v.Db, projectRoleTable, project) +func (v *View) PutProjectRole(role *model.ProjectRoleView, event *models.Event) error { + err := view.PutProjectRole(v.Db, projectRoleTable, role) if err != nil { return err } - return v.ProcessedProjectRoleSequence(project.Sequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } -func (v *View) DeleteProjectRole(projectID, orgID, key string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectRole(projectID, orgID, key string, event *models.Event) error { err := view.DeleteProjectRole(v.Db, projectRoleTable, projectID, orgID, key) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedProjectRoleSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } func (v *View) DeleteProjectRolesByProjectID(projectID string) error { return view.DeleteProjectRolesByProjectID(v.Db, projectRoleTable, projectID) } -func (v *View) GetLatestProjectRoleSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectRoleTable) +func (v *View) GetLatestProjectRoleSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectRoleTable, aggregateType) } -func (v *View) ProcessedProjectRoleSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectRoleTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectRoleSequence(event *models.Event) error { + return v.saveCurrentSequence(projectRoleTable, event) } func (v *View) UpdateProjectRoleSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/sequence.go b/internal/auth/repository/eventsourcing/view/sequence.go index 4ac2ab80cc..d9eb6c7a64 100644 --- a/internal/auth/repository/eventsourcing/view/sequence.go +++ b/internal/auth/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "auth.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/auth/repository/eventsourcing/view/token.go b/internal/auth/repository/eventsourcing/view/token.go index 4cb7c41844..105034a364 100644 --- a/internal/auth/repository/eventsourcing/view/token.go +++ b/internal/auth/repository/eventsourcing/view/token.go @@ -1,10 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_view "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -19,60 +20,60 @@ func (v *View) TokensByUserID(userID string) ([]*model.TokenView, error) { return usr_view.TokensByUserID(v.Db, tokenTable, userID) } -func (v *View) PutToken(token *model.TokenView, eventTimestamp time.Time) error { +func (v *View) PutToken(token *model.TokenView, event *models.Event) error { err := usr_view.PutToken(v.Db, tokenTable, token) if err != nil { return err } - return v.ProcessedTokenSequence(token.Sequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) PutTokens(token []*model.TokenView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutTokens(token []*model.TokenView, event *models.Event) error { err := usr_view.PutTokens(v.Db, tokenTable, token...) if err != nil { return err } - return v.ProcessedTokenSequence(sequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteToken(tokenID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteToken(tokenID string, event *models.Event) error { err := usr_view.DeleteToken(v.Db, tokenTable, tokenID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteSessionTokens(agentID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteSessionTokens(agentID, userID string, event *models.Event) error { err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteUserTokens(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserTokens(userID string, event *models.Event) error { err := usr_view.DeleteUserTokens(v.Db, tokenTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteApplicationTokens(eventSequence uint64, eventTimestamp time.Time, ids ...string) error { +func (v *View) DeleteApplicationTokens(event *models.Event, ids ...string) error { err := usr_view.DeleteApplicationTokens(v.Db, tokenTable, ids) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) GetLatestTokenSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(tokenTable) +func (v *View) GetLatestTokenSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(tokenTable, aggregateType) } -func (v *View) ProcessedTokenSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(tokenTable, eventSequence, eventTimestamp) +func (v *View) ProcessedTokenSequence(event *models.Event) error { + return v.saveCurrentSequence(tokenTable, event) } func (v *View) UpdateTokenSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user.go b/internal/auth/repository/eventsourcing/view/user.go index e8b0591d22..21de8cef8d 100644 --- a/internal/auth/repository/eventsourcing/view/user.go +++ b/internal/auth/repository/eventsourcing/view/user.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -52,36 +53,36 @@ func (v *View) UserMFAs(userID string) ([]*usr_model.MultiFactor, error) { return view.UserMFAs(v.Db, userTable, userID) } -func (v *View) PutUser(user *model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUser(user *model.UserView, event *models.Event) error { err := view.PutUser(v.Db, userTable, user) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) PutUsers(users []*model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUsers(users []*model.UserView, event *models.Event) error { err := view.PutUsers(v.Db, userTable, users...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) DeleteUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) GetLatestUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userTable) +func (v *View) GetLatestUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userTable, aggregateType) } -func (v *View) ProcessedUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSequence(event *models.Event) error { + return v.saveCurrentSequence(userTable, event) } func (v *View) UpdateUserSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user_grant.go b/internal/auth/repository/eventsourcing/view/user_grant.go index 230e9f5c8a..d9a52a586e 100644 --- a/internal/auth/repository/eventsourcing/view/user_grant.go +++ b/internal/auth/repository/eventsourcing/view/user_grant.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" "github.com/caos/zitadel/internal/usergrant/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -36,36 +37,36 @@ func (v *View) SearchUserGrants(request *grant_model.UserGrantSearchRequest) ([] return view.SearchUserGrants(v.Db, userGrantTable, request) } -func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) error { err := view.PutUserGrant(v.Db, userGrantTable, grant) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) PutUserGrants(grants []*model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrants(grants []*model.UserGrantView, event *models.Event) error { err := view.PutUserGrants(v.Db, userGrantTable, grants...) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) DeleteUserGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) GetLatestUserGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userGrantTable) +func (v *View) GetLatestUserGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userGrantTable, aggregateType) } -func (v *View) ProcessedUserGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userGrantTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(userGrantTable, event) } func (v *View) UpdateUserGrantSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user_membership.go b/internal/auth/repository/eventsourcing/view/user_membership.go index 84607584ab..f3d9fae70e 100644 --- a/internal/auth/repository/eventsourcing/view/user_membership.go +++ b/internal/auth/repository/eventsourcing/view/user_membership.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,60 +29,60 @@ func (v *View) SearchUserMemberships(request *usr_model.UserMembershipSearchRequ return view.SearchUserMemberships(v.Db, userMembershipTable, request) } -func (v *View) PutUserMembership(membership *model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserMembership(membership *model.UserMembershipView, event *models.Event) error { err := view.PutUserMembership(v.Db, userMembershipTable, membership) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, event *models.Event) error { err := view.PutUserMemberships(v.Db, userMembershipTable, memberships...) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, event *models.Event) error { err := view.DeleteUserMembership(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByUserID(userID string, event *models.Event) error { err := view.DeleteUserMembershipsByUserID(v.Db, userMembershipTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateIDAndObjectID(v.Db, userMembershipTable, aggregateID, objectID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) GetLatestUserMembershipSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userMembershipTable) +func (v *View) GetLatestUserMembershipSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userMembershipTable, aggregateType) } -func (v *View) ProcessedUserMembershipSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userMembershipTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserMembershipSequence(event *models.Event) error { + return v.saveCurrentSequence(userMembershipTable, event) } func (v *View) UpdateUserMembershipSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/eventsourcing/view/user_session.go b/internal/auth/repository/eventsourcing/view/user_session.go index 805cc04aef..16ce4c7986 100644 --- a/internal/auth/repository/eventsourcing/view/user_session.go +++ b/internal/auth/repository/eventsourcing/view/user_session.go @@ -1,10 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -27,36 +28,36 @@ func (v *View) ActiveUserSessions() ([]*model.UserSessionView, error) { return view.ActiveUserSessions(v.Db, userSessionTable) } -func (v *View) PutUserSession(userSession *model.UserSessionView, eventTimestamp time.Time) error { +func (v *View) PutUserSession(userSession *model.UserSessionView, event *models.Event) error { err := view.PutUserSession(v.Db, userSessionTable, userSession) if err != nil { return err } - return v.ProcessedUserSessionSequence(userSession.Sequence, eventTimestamp) + return v.ProcessedUserSessionSequence(event) } -func (v *View) PutUserSessions(userSession []*model.UserSessionView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserSessions(userSession []*model.UserSessionView, event *models.Event) error { err := view.PutUserSessions(v.Db, userSessionTable, userSession...) if err != nil { return err } - return v.ProcessedUserSessionSequence(sequence, eventTimestamp) + return v.ProcessedUserSessionSequence(event) } -func (v *View) DeleteUserSessions(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserSessions(userID string, event *models.Event) error { err := view.DeleteUserSessions(v.Db, userSessionTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserSessionSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSessionSequence(event) } -func (v *View) GetLatestUserSessionSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userSessionTable) +func (v *View) GetLatestUserSessionSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userSessionTable, aggregateType) } -func (v *View) ProcessedUserSessionSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userSessionTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSessionSequence(event *models.Event) error { + return v.saveCurrentSequence(userSessionTable, event) } func (v *View) UpdateUserSessionSpoolerRunTimestamp() error { diff --git a/internal/auth/repository/user.go b/internal/auth/repository/user.go index 1399a2b6a6..8ae79d92d2 100644 --- a/internal/auth/repository/user.go +++ b/internal/auth/repository/user.go @@ -32,6 +32,7 @@ type UserRepository interface { VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error RemoveMFAU2F(ctx context.Context, userID, webAuthNTokenID string) error + GetPasswordless(ctx context.Context, id string) ([]*model.WebAuthNToken, error) AddPasswordless(ctx context.Context, id string) (*model.WebAuthNToken, error) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error @@ -80,6 +81,7 @@ type myUserRepo interface { VerifyMyMFAU2FSetup(ctx context.Context, tokenName string, data []byte) error RemoveMyMFAU2F(ctx context.Context, webAuthNTokenID string) error + GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) VerifyMyPasswordlessSetup(ctx context.Context, tokenName string, data []byte) error RemoveMyPasswordless(ctx context.Context, webAuthNTokenID string) error diff --git a/internal/auth_request/model/auth_request.go b/internal/auth_request/model/auth_request.go index bd51897afd..df74e85d39 100644 --- a/internal/auth_request/model/auth_request.go +++ b/internal/auth_request/model/auth_request.go @@ -1,11 +1,13 @@ package model import ( - "github.com/caos/zitadel/internal/iam/model" - "golang.org/x/text/language" "strings" "time" + "golang.org/x/text/language" + + "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/errors" ) @@ -30,6 +32,8 @@ type AuthRequest struct { LoginName string DisplayName string UserOrgID string + RequestedOrgID string + RequestedOrgName string SelectedIDPConfigID string LinkingUsers []*ExternalUser PossibleSteps []NextStep diff --git a/internal/auth_request/model/next_step.go b/internal/auth_request/model/next_step.go index 3312244e11..b3a3a07733 100644 --- a/internal/auth_request/model/next_step.go +++ b/internal/auth_request/model/next_step.go @@ -48,10 +48,11 @@ func (s *SelectUserStep) Type() NextStepType { } type UserSelection struct { - UserID string - DisplayName string - LoginName string - UserSessionState UserSessionState + UserID string + DisplayName string + LoginName string + UserSessionState UserSessionState + SelectionPossible bool } type InitUserStep struct { diff --git a/internal/auth_request/model/request.go b/internal/auth_request/model/request.go index 72f10ce49f..43f8c93eaf 100644 --- a/internal/auth_request/model/request.go +++ b/internal/auth_request/model/request.go @@ -20,6 +20,7 @@ const ( const ( OrgDomainPrimaryScope = "urn:zitadel:iam:org:domain:primary:" + OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary" ProjectIDScope = "urn:zitadel:iam:org:project:id:" AudSuffix = ":aud" ) diff --git a/internal/authz/repository/eventsourcing/handler/application.go b/internal/authz/repository/eventsourcing/handler/application.go index 4b5773607e..fcb84ee675 100644 --- a/internal/authz/repository/eventsourcing/handler/application.go +++ b/internal/authz/repository/eventsourcing/handler/application.go @@ -3,27 +3,61 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Application struct { - handler -} - const ( applicationTable = "authz.applications" ) +type Application struct { + handler + subscription *eventstore.Subscription +} + +func newApplication(handler handler) *Application { + h := &Application{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *Application) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (a *Application) ViewModel() string { return applicationTable } +func (a *Application) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (a *Application) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := a.view.GetLatestApplicationSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (a *Application) EventQuery() (*models.SearchQuery, error) { - sequence, err := a.view.GetLatestApplicationSequence() + sequence, err := a.view.GetLatestApplicationSequence("") if err != nil { return nil, err } @@ -54,14 +88,14 @@ func (a *Application) Reduce(event *models.Event) (err error) { if err != nil { return err } - return a.view.DeleteApplication(app.ID, event.Sequence, event.CreationDate) + return a.view.DeleteApplication(app.ID, event) default: - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } if err != nil { return err } - return a.view.PutApplication(app, event.CreationDate) + return a.view.PutApplication(app, event) } func (a *Application) OnError(event *models.Event, spoolerError error) error { diff --git a/internal/authz/repository/eventsourcing/handler/handler.go b/internal/authz/repository/eventsourcing/handler/handler.go index 31274d77ab..ee21d1b020 100644 --- a/internal/authz/repository/eventsourcing/handler/handler.go +++ b/internal/authz/repository/eventsourcing/handler/handler.go @@ -3,13 +3,12 @@ package handler import ( "time" + "github.com/caos/zitadel/internal/authz/repository/eventsourcing/view" sd "github.com/caos/zitadel/internal/config/systemdefaults" + "github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/query" iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - - "github.com/caos/zitadel/internal/authz/repository/eventsourcing/view" - "github.com/caos/zitadel/internal/config/types" ) type Configs map[string]*Config @@ -23,29 +22,35 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { - IamEvents *iam_events.IAMEventstore + IAMEvents *iam_events.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { return []query.Handler{ - &UserGrant{ - handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, - eventstore: eventstore, - iamID: systemDefaults.IamID, - iamEvents: repos.IamEvents, - }, - &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}}, - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, + newUserGrant( + handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, + repos.IAMEvents, + systemDefaults.IamID), + newApplication( + handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}), + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } @@ -54,6 +59,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/authz/repository/eventsourcing/handler/org.go b/internal/authz/repository/eventsourcing/handler/org.go index e487831f02..34cc1a69e7 100644 --- a/internal/authz/repository/eventsourcing/handler/org.go +++ b/internal/authz/repository/eventsourcing/handler/org.go @@ -3,27 +3,62 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "authz.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *Org) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (_ *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (o *Org) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } @@ -53,10 +88,10 @@ func (o *Org) Reduce(event *es_models.Event) error { return err } default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/authz/repository/eventsourcing/handler/user_grant.go b/internal/authz/repository/eventsourcing/handler/user_grant.go index 1c7b829964..acdc5fde9e 100644 --- a/internal/authz/repository/eventsourcing/handler/user_grant.go +++ b/internal/authz/repository/eventsourcing/handler/user_grant.go @@ -11,6 +11,7 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_events "github.com/caos/zitadel/internal/iam/repository/eventsourcing" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" @@ -20,22 +21,59 @@ import ( "github.com/caos/zitadel/internal/v2/domain" ) -type UserGrant struct { - handler - eventstore eventstore.Eventstore - iamEvents *iam_events.IAMEventstore - iamID string - iamProjectID string -} - const ( userGrantTable = "authz.user_grants" ) +type UserGrant struct { + handler + iamEvents *iam_events.IAMEventstore + iamID string + iamProjectID string + subscription *eventstore.Subscription +} + +func newUserGrant( + handler handler, + iamEvents *iam_events.IAMEventstore, + iamID string, +) *UserGrant { + h := &UserGrant{ + handler: handler, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *UserGrant) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (u *UserGrant) ViewModel() string { return userGrantTable } +func (_ *UserGrant) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate} +} + +func (u *UserGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { if u.iamProjectID == "" { err := u.setIamProjectID() @@ -43,7 +81,7 @@ func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { return nil, err } } - sequence, err := u.view.GetLatestUserGrantSequence() + sequence, err := u.view.GetLatestUserGrantSequence("") if err != nil { return nil, err } @@ -75,7 +113,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "PROJECT_GRANT", member.GrantID, member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -86,7 +124,7 @@ func (u *UserGrant) processOrg(event *models.Event) (err error) { member.SetData(event) return u.processMember(event, "ORG", "", member.UserID, member.Roles) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -124,16 +162,16 @@ func (u *UserGrant) processIAMMember(event *models.Event, rolePrefix string, suf } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case iam_es_model.IAMMemberRemoved: member.SetData(event) grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID) if err != nil { return err } - return u.view.DeleteUserGrant(grant.ID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(grant.ID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } @@ -169,7 +207,7 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st } grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) case org_es_model.OrgMemberRemoved, proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectGrantMemberRemoved: @@ -179,18 +217,18 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st return err } if errors.IsNotFound(err) { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if roleSuffix != "" { roleKeys = suffixRoles(roleSuffix, roleKeys) } if grant.RoleKeys == nil { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } grant.RoleKeys = mergeExistingRoles(rolePrefix, roleSuffix, grant.RoleKeys, nil) - return u.view.PutUserGrant(grant, event.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } } diff --git a/internal/authz/repository/eventsourcing/repository.go b/internal/authz/repository/eventsourcing/repository.go index 922290cd2d..b6ad0d7680 100644 --- a/internal/authz/repository/eventsourcing/repository.go +++ b/internal/authz/repository/eventsourcing/repository.go @@ -84,7 +84,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults) (* return nil, err } - repos := handler.EventstoreRepos{IamEvents: iam} + repos := handler.EventstoreRepos{IAMEvents: iam} spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, repos, systemDefaults) return &EsRepository{ diff --git a/internal/authz/repository/eventsourcing/view/application.go b/internal/authz/repository/eventsourcing/view/application.go index a8e772b58d..84ae564981 100644 --- a/internal/authz/repository/eventsourcing/view/application.go +++ b/internal/authz/repository/eventsourcing/view/application.go @@ -2,8 +2,9 @@ package view import ( "context" - "time" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" @@ -34,28 +35,28 @@ func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) return view.SearchApplications(v.Db, applicationTable, request) } -func (v *View) PutApplication(project *model.ApplicationView, eventTimestamp time.Time) error { +func (v *View) PutApplication(project *model.ApplicationView, event *models.Event) error { err := view.PutApplication(v.Db, applicationTable, project) if err != nil { return err } - return v.ProcessedApplicationSequence(project.Sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) DeleteApplication(appID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedApplicationSequence(eventSequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(applicationTable) +func (v *View) GetLatestApplicationSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(applicationTable, aggregateType) } -func (v *View) ProcessedApplicationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(applicationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedApplicationSequence(event *models.Event) error { + return v.saveCurrentSequence(applicationTable, event) } func (v *View) UpdateApplicationSpoolerRunTimestamp() error { diff --git a/internal/authz/repository/eventsourcing/view/org.go b/internal/authz/repository/eventsourcing/view/org.go index f886123b2e..6d27b5b071 100644 --- a/internal/authz/repository/eventsourcing/view/org.go +++ b/internal/authz/repository/eventsourcing/view/org.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/org/model" org_view "github.com/caos/zitadel/internal/org/repository/view" org_model "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -20,12 +20,12 @@ func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, ui return org_view.SearchOrgs(v.Db, orgTable, req) } -func (v *View) PutOrg(org *org_model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *org_model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -36,12 +36,12 @@ func (v *View) ProcessedOrgFailedEvent(failedEvent *repository.FailedEvent) erro return v.saveFailedEvent(failedEvent) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } func (v *View) UpdateOrgSpoolerRunTimestamp() error { diff --git a/internal/authz/repository/eventsourcing/view/sequence.go b/internal/authz/repository/eventsourcing/view/sequence.go index ad628fbf7b..42e6e20bfd 100644 --- a/internal/authz/repository/eventsourcing/view/sequence.go +++ b/internal/authz/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "authz.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/authz/repository/eventsourcing/view/token.go b/internal/authz/repository/eventsourcing/view/token.go index bab80f238a..d2987a8194 100644 --- a/internal/authz/repository/eventsourcing/view/token.go +++ b/internal/authz/repository/eventsourcing/view/token.go @@ -1,10 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_view "github.com/caos/zitadel/internal/user/repository/view" usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -15,36 +16,36 @@ func (v *View) TokenByID(tokenID string) (*usr_view_model.TokenView, error) { return usr_view.TokenByID(v.Db, tokenTable, tokenID) } -func (v *View) PutToken(token *usr_view_model.TokenView, eventTimestamp time.Time) error { +func (v *View) PutToken(token *usr_view_model.TokenView, event *models.Event) error { err := usr_view.PutToken(v.Db, tokenTable, token) if err != nil { return err } - return v.ProcessedTokenSequence(token.Sequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteToken(tokenID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteToken(tokenID string, event *models.Event) error { err := usr_view.DeleteToken(v.Db, tokenTable, tokenID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) DeleteSessionTokens(agentID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteSessionTokens(agentID, userID string, event *models.Event) error { err := usr_view.DeleteSessionTokens(v.Db, tokenTable, agentID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedTokenSequence(eventSequence, eventTimestamp) + return v.ProcessedTokenSequence(event) } -func (v *View) GetLatestTokenSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(tokenTable) +func (v *View) GetLatestTokenSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(tokenTable, aggregateType) } -func (v *View) ProcessedTokenSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(tokenTable, eventSequence, eventTimestamp) +func (v *View) ProcessedTokenSequence(event *models.Event) error { + return v.saveCurrentSequence(tokenTable, event) } func (v *View) UpdateTokenSpoolerRunTimestamp() error { diff --git a/internal/authz/repository/eventsourcing/view/user_grant.go b/internal/authz/repository/eventsourcing/view/user_grant.go index 68154dc3c9..84de9319ea 100644 --- a/internal/authz/repository/eventsourcing/view/user_grant.go +++ b/internal/authz/repository/eventsourcing/view/user_grant.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" "github.com/caos/zitadel/internal/usergrant/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,28 +33,28 @@ func (v *View) SearchUserGrants(request *grant_model.UserGrantSearchRequest) ([] return view.SearchUserGrants(v.Db, userGrantTable, request) } -func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) error { err := view.PutUserGrant(v.Db, userGrantTable, grant) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) DeleteUserGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) GetLatestUserGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userGrantTable) +func (v *View) GetLatestUserGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userGrantTable, aggregateType) } -func (v *View) ProcessedUserGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userGrantTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(userGrantTable, event) } func (v *View) UpdateUserGrantSpoolerRunTimestamp() error { diff --git a/internal/config/types/sql.go b/internal/config/types/sql.go index 74597c7e84..461dda6e78 100644 --- a/internal/config/types/sql.go +++ b/internal/config/types/sql.go @@ -62,6 +62,10 @@ func (s *SQL) Start() (*sql.DB, error) { if err != nil { return nil, errors.ThrowPreconditionFailed(err, "TYPES-9qBtr", "unable to open database connection") } + // as we open many sql clients we set the max + // open cons deep. now 3(maxconn) * 8(clients) = max 24 conns per pod + client.SetMaxOpenConns(3) + client.SetMaxIdleConns(3) return client, nil } diff --git a/internal/crypto/aes.go b/internal/crypto/aes.go index 0aca064941..41720b04f6 100644 --- a/internal/crypto/aes.go +++ b/internal/crypto/aes.go @@ -116,7 +116,10 @@ func DecryptAESString(data string, key string) (string, error) { return string(decrypted), nil } -func DecryptAES(cipherText []byte, key string) ([]byte, error) { +func DecryptAES(text []byte, key string) ([]byte, error) { + cipherText := make([]byte, len(text)) + copy(cipherText, text) + block, err := aes.NewCipher([]byte(key)) if err != nil { return nil, err diff --git a/internal/eventstore/eventstore.go b/internal/eventstore/eventstore.go index b4f65d165a..84665dfc77 100644 --- a/internal/eventstore/eventstore.go +++ b/internal/eventstore/eventstore.go @@ -16,6 +16,7 @@ type Eventstore interface { FilterEvents(ctx context.Context, searchQuery *models.SearchQuery) (events []*models.Event, err error) LatestSequence(ctx context.Context, searchQuery *models.SearchQueryFactory) (uint64, error) V2() *es_v2.Eventstore + Subscribe(aggregates ...models.AggregateType) *Subscription } var _ Eventstore = (*eventstore)(nil) @@ -46,6 +47,8 @@ func (es *eventstore) PushAggregates(ctx context.Context, aggregates ...*models. if err != nil { return err } + + go notify(aggregates) return nil } diff --git a/internal/eventstore/mock/eventstore.mock.go b/internal/eventstore/mock/eventstore.mock.go index 18fb7a949a..fbe1990856 100644 --- a/internal/eventstore/mock/eventstore.mock.go +++ b/internal/eventstore/mock/eventstore.mock.go @@ -6,6 +6,7 @@ package mock import ( context "context" + eventstore "github.com/caos/zitadel/internal/eventstore" models "github.com/caos/zitadel/internal/eventstore/models" gomock "github.com/golang/mock/gomock" reflect "reflect" @@ -110,3 +111,21 @@ func (mr *MockEventstoreMockRecorder) PushAggregates(arg0 interface{}, arg1 ...i varargs := append([]interface{}{arg0}, arg1...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PushAggregates", reflect.TypeOf((*MockEventstore)(nil).PushAggregates), varargs...) } + +// Subscribe mocks base method +func (m *MockEventstore) Subscribe(arg0 ...models.AggregateType) *eventstore.Subscription { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range arg0 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Subscribe", varargs...) + ret0, _ := ret[0].(*eventstore.Subscription) + return ret0 +} + +// Subscribe indicates an expected call of Subscribe +func (mr *MockEventstoreMockRecorder) Subscribe(arg0 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockEventstore)(nil).Subscribe), arg0...) +} diff --git a/internal/eventstore/models/aggregate.go b/internal/eventstore/models/aggregate.go index 66026b05d5..a0a5c88779 100644 --- a/internal/eventstore/models/aggregate.go +++ b/internal/eventstore/models/aggregate.go @@ -27,6 +27,10 @@ type Aggregate struct { Precondition *precondition } +func (a *Aggregate) Type() AggregateType { + return a.typ +} + type precondition struct { Query *SearchQuery Validation func(...*Event) error diff --git a/internal/eventstore/models/object.go b/internal/eventstore/models/object.go index 695610c45c..931014f73b 100644 --- a/internal/eventstore/models/object.go +++ b/internal/eventstore/models/object.go @@ -15,6 +15,8 @@ type ObjectRoot struct { func (o *ObjectRoot) AppendEvent(event *Event) { if o.AggregateID == "" { o.AggregateID = event.AggregateID + } else if o.AggregateID != event.AggregateID { + return } if o.ResourceOwner == "" { o.ResourceOwner = event.ResourceOwner diff --git a/internal/eventstore/models/search_query.go b/internal/eventstore/models/search_query.go index 3530db46a8..06cbf97f1d 100644 --- a/internal/eventstore/models/search_query.go +++ b/internal/eventstore/models/search_query.go @@ -11,7 +11,8 @@ type SearchQueryFactory struct { desc bool aggregateTypes []AggregateType aggregateIDs []string - eventSequence uint64 + sequenceFrom uint64 + sequenceTo uint64 eventTypes []EventType resourceOwner string } @@ -51,7 +52,11 @@ func FactoryFromSearchQuery(query *SearchQuery) *SearchQueryFactory { factory = factory.AggregateIDs(aggregateIDs...) } case Field_LatestSequence: - factory = factory.SequenceGreater(filter.value.(uint64)) + if filter.operation == Operation_Greater { + factory = factory.SequenceGreater(filter.value.(uint64)) + } else { + factory = factory.SequenceLess(filter.value.(uint64)) + } case Field_ResourceOwner: factory = factory.ResourceOwner(filter.value.(string)) case Field_EventType: @@ -82,7 +87,12 @@ func (factory *SearchQueryFactory) Limit(limit uint64) *SearchQueryFactory { } func (factory *SearchQueryFactory) SequenceGreater(sequence uint64) *SearchQueryFactory { - factory.eventSequence = sequence + factory.sequenceFrom = sequence + return factory +} + +func (factory *SearchQueryFactory) SequenceLess(sequence uint64) *SearchQueryFactory { + factory.sequenceTo = sequence return factory } @@ -128,7 +138,8 @@ func (factory *SearchQueryFactory) Build() (*searchQuery, error) { for _, f := range []func() *Filter{ factory.aggregateIDFilter, - factory.eventSequenceFilter, + factory.sequenceFromFilter, + factory.sequenceToFilter, factory.eventTypeFilter, factory.resourceOwnerFilter, } { @@ -172,15 +183,26 @@ func (factory *SearchQueryFactory) aggregateTypeFilter() *Filter { return NewFilter(Field_AggregateType, factory.aggregateTypes, Operation_In) } -func (factory *SearchQueryFactory) eventSequenceFilter() *Filter { - if factory.eventSequence == 0 { +func (factory *SearchQueryFactory) sequenceFromFilter() *Filter { + if factory.sequenceFrom == 0 { return nil } sortOrder := Operation_Greater if factory.desc { sortOrder = Operation_Less } - return NewFilter(Field_LatestSequence, factory.eventSequence, sortOrder) + return NewFilter(Field_LatestSequence, factory.sequenceFrom, sortOrder) +} + +func (factory *SearchQueryFactory) sequenceToFilter() *Filter { + if factory.sequenceTo == 0 { + return nil + } + sortOrder := Operation_Less + if factory.desc { + sortOrder = Operation_Greater + } + return NewFilter(Field_LatestSequence, factory.sequenceTo, sortOrder) } func (factory *SearchQueryFactory) resourceOwnerFilter() *Filter { diff --git a/internal/eventstore/models/search_query_old.go b/internal/eventstore/models/search_query_old.go index c803fac64b..1748b9d4d7 100644 --- a/internal/eventstore/models/search_query_old.go +++ b/internal/eventstore/models/search_query_old.go @@ -58,13 +58,19 @@ func (q *SearchQuery) LatestSequenceFilter(sequence uint64) *SearchQuery { return q.setFilter(NewFilter(Field_LatestSequence, sequence, sortOrder)) } +func (q *SearchQuery) SequenceBetween(from, to uint64) *SearchQuery { + q.setFilter(NewFilter(Field_LatestSequence, from, Operation_Greater)) + q.setFilter(NewFilter(Field_LatestSequence, to, Operation_Less)) + return q +} + func (q *SearchQuery) ResourceOwnerFilter(resourceOwner string) *SearchQuery { return q.setFilter(NewFilter(Field_ResourceOwner, resourceOwner, Operation_Equals)) } func (q *SearchQuery) setFilter(filter *Filter) *SearchQuery { for i, f := range q.Filters { - if f.field == filter.field { + if f.field == filter.field && f.field != Field_LatestSequence { q.Filters[i] = filter return q } diff --git a/internal/eventstore/models/search_query_test.go b/internal/eventstore/models/search_query_test.go index ac9380b4d9..b52f6490cf 100644 --- a/internal/eventstore/models/search_query_test.go +++ b/internal/eventstore/models/search_query_test.go @@ -103,7 +103,7 @@ func TestSearchQueryFactorySetters(t *testing.T) { setters: []func(*SearchQueryFactory) *SearchQueryFactory{testSetSequence(90)}, }, res: &SearchQueryFactory{ - eventSequence: 90, + sequenceFrom: 90, }, }, { diff --git a/internal/eventstore/query/handler.go b/internal/eventstore/query/handler.go index 36bba2547e..622a69156e 100755 --- a/internal/eventstore/query/handler.go +++ b/internal/eventstore/query/handler.go @@ -1,11 +1,18 @@ package query import ( + "context" "time" + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" ) +const ( + eventLimit = 10000 +) + type Handler interface { ViewModel() string EventQuery() (*models.SearchQuery, error) @@ -13,5 +20,63 @@ type Handler interface { OnError(event *models.Event, err error) error OnSuccess() error MinimumCycleDuration() time.Duration + LockDuration() time.Duration QueryLimit() uint64 + + AggregateTypes() []models.AggregateType + CurrentSequence(*models.Event) (uint64, error) + Eventstore() eventstore.Eventstore +} + +func ReduceEvent(handler Handler, event *models.Event) { + currentSequence, err := handler.CurrentSequence(event) + if err != nil { + logging.Log("HANDL-BmpkC").WithError(err).Warn("unable to get current sequence") + return + } + + searchQuery := models.NewSearchQuery(). + AggregateTypeFilter(handler.AggregateTypes()...). + SequenceBetween(currentSequence, event.Sequence). + SetLimit(eventLimit) + + unprocessedEvents, err := handler.Eventstore().FilterEvents(context.Background(), searchQuery) + if err != nil { + logging.LogWithFields("HANDL-L6YH1", "seq", event.Sequence).Warn("filter failed") + return + } + + processedSequences := map[models.AggregateType]uint64{} + + for _, unprocessedEvent := range unprocessedEvents { + currentSequence, err := handler.CurrentSequence(unprocessedEvent) + if err != nil { + logging.Log("HANDL-BmpkC").WithError(err).Warn("unable to get current sequence") + return + } + _, ok := processedSequences[unprocessedEvent.AggregateType] + if !ok { + processedSequences[unprocessedEvent.AggregateType] = currentSequence + } + if processedSequences[unprocessedEvent.AggregateType] != currentSequence { + if currentSequence < processedSequences[unprocessedEvent.AggregateType] { + logging.LogWithFields("QUERY-DOYVN", + "processed", processedSequences[unprocessedEvent.AggregateType], + "current", currentSequence, + "view", handler.ViewModel()). + Warn("sequence not matching") + } + return + } + + err = handler.Reduce(unprocessedEvent) + logging.LogWithFields("HANDL-V42TI", "seq", unprocessedEvent.Sequence).OnError(err).Warn("reduce failed") + processedSequences[unprocessedEvent.AggregateType] = unprocessedEvent.Sequence + } + if len(unprocessedEvents) == eventLimit { + logging.LogWithFields("QUERY-BSqe9", "seq", event.Sequence).Warn("didnt process event") + return + } + err = handler.Reduce(event) + logging.LogWithFields("HANDL-wQDL2", "seq", event.Sequence).OnError(err).Warn("reduce failed") } diff --git a/internal/eventstore/spooler/config.go b/internal/eventstore/spooler/config.go index fa796f06c3..e89399b38e 100644 --- a/internal/eventstore/spooler/config.go +++ b/internal/eventstore/spooler/config.go @@ -1,6 +1,7 @@ package spooler import ( + "math/rand" "os" "github.com/caos/logging" @@ -23,6 +24,11 @@ func (c *Config) New() *Spooler { logging.Log("SPOOL-bdO56").OnError(err).Panic("unable to generate lockID") } + //shuffle the handlers for better balance when running multiple pods + rand.Shuffle(len(c.ViewHandlers), func(i, j int) { + c.ViewHandlers[i], c.ViewHandlers[j] = c.ViewHandlers[j], c.ViewHandlers[i] + }) + return &Spooler{ handlers: c.ViewHandlers, lockID: lockID, diff --git a/internal/eventstore/spooler/spooler.go b/internal/eventstore/spooler/spooler.go index fb480f7916..40d124a6ed 100644 --- a/internal/eventstore/spooler/spooler.go +++ b/internal/eventstore/spooler/spooler.go @@ -4,6 +4,7 @@ import ( "context" "strconv" "sync" + "time" "github.com/caos/logging" "github.com/caos/zitadel/internal/eventstore" @@ -11,8 +12,6 @@ import ( "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/view/repository" - - "time" ) type Spooler struct { @@ -71,14 +70,26 @@ func (s *spooledHandler) load(workerID string) { hasLocked := s.lock(ctx, errs, workerID) if <-hasLocked { - events, err := s.query(ctx) - if err != nil { - errs <- err - } else { - errs <- s.process(ctx, events, workerID) - logging.Log("SPOOL-0pV8o").WithField("view", s.ViewModel()).WithField("worker", workerID).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("process done") + for { + events, err := s.query(ctx) + if err != nil { + errs <- err + break + } + err = s.process(ctx, events, workerID) + if err != nil { + errs <- err + break + } + if uint64(len(events)) < s.QueryLimit() { + // no more events to process + // stop chan + if ctx.Err() == nil { + errs <- nil + } + break + } } - } <-ctx.Done() } @@ -92,14 +103,19 @@ func (s *spooledHandler) awaitError(cancel func(), errs chan error, workerID str } func (s *spooledHandler) process(ctx context.Context, events []*models.Event, workerID string) error { - for _, event := range events { + for i, event := range events { select { case <-ctx.Done(): logging.LogWithFields("SPOOL-FTKwH", "view", s.ViewModel(), "worker", workerID, "traceID", tracing.TraceIDFromCtx(ctx)).Debug("context canceled") return nil default: if err := s.Reduce(event); err != nil { - return s.OnError(event, err) + err = s.OnError(event, err) + if err == nil { + continue + } + time.Sleep(100 * time.Millisecond) + return s.process(ctx, events[i:], workerID) } } } @@ -144,12 +160,12 @@ func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID s case <-ctx.Done(): return case <-renewTimer: - err := s.locker.Renew(workerID, s.ViewModel(), s.MinimumCycleDuration()*2) + err := s.locker.Renew(workerID, s.ViewModel(), s.LockDuration()) firstLock.Do(func() { locked <- err == nil }) if err == nil { - renewTimer = time.After(s.MinimumCycleDuration()) + renewTimer = time.After(s.LockDuration()) continue } @@ -167,7 +183,8 @@ func (s *spooledHandler) lock(ctx context.Context, errs chan<- error, workerID s func HandleError(event *models.Event, failedErr error, latestFailedEvent func(sequence uint64) (*repository.FailedEvent, error), processFailedEvent func(*repository.FailedEvent) error, - processSequence func(uint64, time.Time) error, errorCountUntilSkip uint64) error { + processSequence func(*models.Event) error, + errorCountUntilSkip uint64) error { failedEvent, err := latestFailedEvent(event.Sequence) if err != nil { return err @@ -179,9 +196,9 @@ func HandleError(event *models.Event, failedErr error, return err } if errorCountUntilSkip <= failedEvent.FailureCount { - return processSequence(event.Sequence, event.CreationDate) + return processSequence(event) } - return nil + return failedErr } func HandleSuccess(updateSpoolerRunTimestamp func() error) error { diff --git a/internal/eventstore/spooler/spooler_test.go b/internal/eventstore/spooler/spooler_test.go index 721ad74e26..5b02977ea1 100644 --- a/internal/eventstore/spooler/spooler_test.go +++ b/internal/eventstore/spooler/spooler_test.go @@ -22,30 +22,57 @@ type testHandler struct { queryError error viewModel string bulkLimit uint64 + maxErrCount int +} + +func (h *testHandler) AggregateTypes() []models.AggregateType { + return nil +} + +func (h *testHandler) CurrentSequence(event *models.Event) (uint64, error) { + return 0, nil +} + +func (h *testHandler) Eventstore() eventstore.Eventstore { + return nil } func (h *testHandler) ViewModel() string { return h.viewModel } + func (h *testHandler) EventQuery() (*models.SearchQuery, error) { if h.queryError != nil { return nil, h.queryError } return &models.SearchQuery{}, nil } + func (h *testHandler) Reduce(*models.Event) error { <-time.After(h.processSleep) return h.processError } + func (h *testHandler) OnError(event *models.Event, err error) error { + if h.maxErrCount == 2 { + return nil + } + h.maxErrCount++ return err } + func (h *testHandler) OnSuccess() error { return nil } + func (h *testHandler) MinimumCycleDuration() time.Duration { return h.cycleDuration } + +func (h *testHandler) LockDuration() time.Duration { + return h.cycleDuration / 2 +} + func (h *testHandler) QueryLimit() uint64 { return h.bulkLimit } @@ -55,6 +82,8 @@ type eventstoreStub struct { err error } +func (es *eventstoreStub) Subscribe(...models.AggregateType) *eventstore.Subscription { return nil } + func (es *eventstoreStub) Health(ctx context.Context) error { return nil } @@ -79,17 +108,18 @@ func (es *eventstoreStub) LatestSequence(ctx context.Context, in *models.SearchQ func TestSpooler_process(t *testing.T) { type fields struct { - currentHandler query.Handler + currentHandler *testHandler } type args struct { timeout time.Duration events []*models.Event } tests := []struct { - name string - fields fields - args args - wantErr bool + name string + fields fields + args args + wantErr bool + wantRetries int }{ { name: "process all events", @@ -121,7 +151,8 @@ func TestSpooler_process(t *testing.T) { args: args{ events: []*models.Event{{}, {}}, }, - wantErr: true, + wantErr: false, + wantRetries: 2, }, } for _, tt := range tests { @@ -140,6 +171,9 @@ func TestSpooler_process(t *testing.T) { if err := s.process(ctx, tt.args.events, "test"); (err != nil) != tt.wantErr { t.Errorf("Spooler.process() error = %v, wantErr %v", err, tt.wantErr) } + if tt.fields.currentHandler.maxErrCount != tt.wantRetries { + t.Errorf("Spooler.process() wrong retry count got: %d want %d", tt.fields.currentHandler.maxErrCount, tt.wantRetries) + } elapsed := time.Since(start).Round(1 * time.Second) if tt.args.timeout != 0 && elapsed != tt.args.timeout { @@ -208,31 +242,31 @@ func TestSpooler_load(t *testing.T) { { "lock exists", fields{ - currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("lock already exists"), 2000*time.Millisecond), + currentHandler: &testHandler{processSleep: 500 * time.Millisecond, viewModel: "testView1", cycleDuration: 1 * time.Second, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView1").expectRenew(t, fmt.Errorf("lock already exists"), 500*time.Millisecond), }, }, { "lock fails", fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 1 * time.Second}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("fail"), 2000*time.Millisecond), + currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView2", cycleDuration: 1 * time.Second, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView2").expectRenew(t, fmt.Errorf("fail"), 500*time.Millisecond), eventstore: &eventstoreStub{events: []*models.Event{{}}}, }, }, { "query fails", fields{ - currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 2000*time.Millisecond), + currentHandler: &testHandler{processSleep: 100 * time.Millisecond, viewModel: "testView3", queryError: fmt.Errorf("query fail"), cycleDuration: 1 * time.Second, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView3").expectRenew(t, nil, 500*time.Millisecond), eventstore: &eventstoreStub{err: fmt.Errorf("fail")}, }, }, { "process event fails", fields{ - currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView", cycleDuration: 500 * time.Millisecond}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 1000*time.Millisecond), + currentHandler: &testHandler{processError: fmt.Errorf("oups"), processSleep: 100 * time.Millisecond, viewModel: "testView4", cycleDuration: 500 * time.Millisecond, bulkLimit: 10}, + locker: newTestLocker(t, "testID", "testView4").expectRenew(t, nil, 250*time.Millisecond), eventstore: &eventstoreStub{events: []*models.Event{{}}}, }, }, @@ -268,7 +302,7 @@ func TestSpooler_lock(t *testing.T) { "renew correct", fields{ currentHandler: &testHandler{cycleDuration: 1 * time.Second, viewModel: "testView"}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 2000*time.Millisecond), + locker: newTestLocker(t, "testID", "testView").expectRenew(t, nil, 500*time.Millisecond), expectsErr: false, }, args{ @@ -279,7 +313,7 @@ func TestSpooler_lock(t *testing.T) { "renew fails", fields{ currentHandler: &testHandler{cycleDuration: 900 * time.Millisecond, viewModel: "testView"}, - locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("renew failed"), 1800*time.Millisecond), + locker: newTestLocker(t, "testID", "testView").expectRenew(t, fmt.Errorf("renew failed"), 450*time.Millisecond), expectsErr: true, }, args{ @@ -333,13 +367,15 @@ func newTestLocker(t *testing.T, lockerID, viewName string) *testLocker { } func (l *testLocker) expectRenew(t *testing.T, err error, waitTime time.Duration) *testLocker { + t.Helper() l.mock.EXPECT().Renew(gomock.Any(), l.viewName, gomock.Any()).DoAndReturn( func(_, _ string, gotten time.Duration) error { + t.Helper() if waitTime-gotten != 0 { t.Errorf("expected waittime %v got %v", waitTime, gotten) } return err - }).Times(1) + }).MinTimes(1).MaxTimes(3) return l } @@ -419,6 +455,7 @@ func TestHandleError(t *testing.T) { }, res: res{ shouldProcessSequence: false, + wantErr: true, }, }, } @@ -432,7 +469,7 @@ func TestHandleError(t *testing.T) { func(*repository.FailedEvent) error { return nil }, - func(uint64, time.Time) error { + func(*models.Event) error { processedSequence = true return nil }, diff --git a/internal/eventstore/subscription.go b/internal/eventstore/subscription.go new file mode 100644 index 0000000000..37aa0dd45c --- /dev/null +++ b/internal/eventstore/subscription.go @@ -0,0 +1,73 @@ +package eventstore + +import ( + "sync" + + "github.com/caos/zitadel/internal/eventstore/models" +) + +var ( + subscriptions map[models.AggregateType][]*Subscription = map[models.AggregateType][]*Subscription{} + subsMutext sync.Mutex +) + +type Subscription struct { + Events chan *models.Event + aggregates []models.AggregateType +} + +func (es *eventstore) Subscribe(aggregates ...models.AggregateType) *Subscription { + events := make(chan *models.Event, 100) + sub := &Subscription{ + Events: events, + aggregates: aggregates, + } + + subsMutext.Lock() + defer subsMutext.Unlock() + + for _, aggregate := range aggregates { + _, ok := subscriptions[aggregate] + if !ok { + subscriptions[aggregate] = make([]*Subscription, 0, 1) + } + subscriptions[aggregate] = append(subscriptions[aggregate], sub) + } + + return sub +} + +func notify(aggregates []*models.Aggregate) { + subsMutext.Lock() + defer subsMutext.Unlock() + for _, aggregate := range aggregates { + subs, ok := subscriptions[aggregate.Type()] + if !ok { + continue + } + for _, sub := range subs { + for _, event := range aggregate.Events { + sub.Events <- event + } + } + } +} + +func (s *Subscription) Unsubscribe() { + subsMutext.Lock() + defer subsMutext.Unlock() + for _, aggregate := range s.aggregates { + subs, ok := subscriptions[aggregate] + if !ok { + continue + } + for i := len(subs) - 1; i >= 0; i-- { + if subs[i] == s { + subs[i] = subs[len(subs)-1] + subs[len(subs)-1] = nil + subs = subs[:len(subs)-1] + } + } + } + close(s.Events) +} diff --git a/internal/iam/model/iam.go b/internal/iam/model/iam.go index 19696f3437..ce88ed38c5 100644 --- a/internal/iam/model/iam.go +++ b/internal/iam/model/iam.go @@ -16,6 +16,7 @@ const ( Step6 Step7 Step8 + Step9 //StepCount marks the the length of possible steps (StepCount-1 == last possible step) StepCount ) diff --git a/internal/iam/repository/eventsourcing/eventstore.go b/internal/iam/repository/eventsourcing/eventstore.go index 3d66ed4dcd..a484a5d573 100644 --- a/internal/iam/repository/eventsourcing/eventstore.go +++ b/internal/iam/repository/eventsourcing/eventstore.go @@ -525,20 +525,31 @@ func (es *IAMEventstore) AddLoginPolicy(ctx context.Context, policy *iam_model.L return model.LoginPolicyToModel(repoIam.DefaultLoginPolicy), nil } -func (es *IAMEventstore) ChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) { +func (es *IAMEventstore) PrepareChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*model.IAM, *models.Aggregate, error) { if policy == nil || !policy.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-3M0so", "Errors.IAM.LoginPolicyInvalid") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-3M0so", "Errors.IAM.LoginPolicyInvalid") } iam, err := es.IAMByID(ctx, policy.AggregateID) if err != nil { - return nil, err + return nil, nil, err } repoIam := model.IAMFromModel(iam) repoLoginPolicy := model.LoginPolicyFromModel(policy) - addAggregate := LoginPolicyChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoLoginPolicy) - err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate) + changeAgg, err := LoginPolicyChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoLoginPolicy)(ctx) + if err != nil { + return nil, nil, err + } + return repoIam, changeAgg, nil +} + +func (es *IAMEventstore) ChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) { + repoIam, changeAggregate, err := es.PrepareChangeLoginPolicy(ctx, policy) + if err != nil { + return nil, err + } + err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIam.AppendEvents, changeAggregate) if err != nil { return nil, err } @@ -665,27 +676,38 @@ func (es *IAMEventstore) RemoveSecondFactorFromLoginPolicy(ctx context.Context, return nil } -func (es *IAMEventstore) AddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (iam_model.MultiFactorType, error) { +func (es *IAMEventstore) PrepareAddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (*model.IAM, *models.Aggregate, error) { if mfa == iam_model.MultiFactorTypeUnspecified { - return 0, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2Dh7J", "Errors.IAM.LoginPolicy.MFA.Unspecified") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2Dh7J", "Errors.IAM.LoginPolicy.MFA.Unspecified") } iam, err := es.IAMByID(ctx, aggregateID) if err != nil { - return 0, err + return nil, nil, err } if _, m := iam.DefaultLoginPolicy.GetMultiFactor(mfa); m != 0 { - return 0, caos_errs.ThrowAlreadyExists(nil, "EVENT-4Rk09", "Errors.IAM.LoginPolicy.MFA.AlreadyExists") + return nil, nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-4Rk09", "Errors.IAM.LoginPolicy.MFA.AlreadyExists") } repoIam := model.IAMFromModel(iam) repoMFA := model.MultiFactorFromModel(mfa) - addAggregate := LoginPolicyMultiFactorAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMFA) - err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate) + addAggregate, err := LoginPolicyMultiFactorAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMFA)(ctx) + if err != nil { + return nil, nil, err + } + return repoIam, addAggregate, nil +} + +func (es *IAMEventstore) AddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (iam_model.MultiFactorType, error) { + repoIAM, addAggregate, err := es.PrepareAddMultiFactorToLoginPolicy(ctx, aggregateID, mfa) if err != nil { return 0, err } - es.iamCache.cacheIAM(repoIam) - if _, m := model.GetMFA(repoIam.DefaultLoginPolicy.MultiFactors, int32(mfa)); m != 0 { + err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIAM.AppendEvents, addAggregate) + if err != nil { + return 0, err + } + es.iamCache.cacheIAM(repoIAM) + if _, m := model.GetMFA(repoIAM.DefaultLoginPolicy.MultiFactors, int32(mfa)); m != 0 { return iam_model.MultiFactorType(m), nil } return 0, caos_errs.ThrowInternal(nil, "EVENT-5N9so", "Errors.Internal") diff --git a/internal/iam/repository/eventsourcing/model/login_policy.go b/internal/iam/repository/eventsourcing/model/login_policy.go index 709b201850..4541cf4de1 100644 --- a/internal/iam/repository/eventsourcing/model/login_policy.go +++ b/internal/iam/repository/eventsourcing/model/login_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/org_iam_policy.go b/internal/iam/repository/eventsourcing/model/org_iam_policy.go index 3056750803..1612d84c12 100644 --- a/internal/iam/repository/eventsourcing/model/org_iam_policy.go +++ b/internal/iam/repository/eventsourcing/model/org_iam_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/password_age_policy.go b/internal/iam/repository/eventsourcing/model/password_age_policy.go index 86c65247d9..7d4b96d4d4 100644 --- a/internal/iam/repository/eventsourcing/model/password_age_policy.go +++ b/internal/iam/repository/eventsourcing/model/password_age_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/password_complexity_policy.go b/internal/iam/repository/eventsourcing/model/password_complexity_policy.go index 37dc2db784..148db9f422 100644 --- a/internal/iam/repository/eventsourcing/model/password_complexity_policy.go +++ b/internal/iam/repository/eventsourcing/model/password_complexity_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/iam/repository/eventsourcing/model/password_lockout_policy.go b/internal/iam/repository/eventsourcing/model/password_lockout_policy.go index e4878c82f9..15c78c45ac 100644 --- a/internal/iam/repository/eventsourcing/model/password_lockout_policy.go +++ b/internal/iam/repository/eventsourcing/model/password_lockout_policy.go @@ -2,6 +2,7 @@ package model import ( "encoding/json" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" diff --git a/internal/management/repository/eventsourcing/eventstore/org.go b/internal/management/repository/eventsourcing/eventstore/org.go index 0d8d3de3a0..4cd80676aa 100644 --- a/internal/management/repository/eventsourcing/eventstore/org.go +++ b/internal/management/repository/eventsourcing/eventstore/org.go @@ -108,7 +108,7 @@ func (repo *OrgRepository) GetMyOrgIamPolicy(ctx context.Context) (*iam_model.Or func (repo *OrgRepository) SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.Queries = append(request.Queries, &org_model.OrgDomainSearchQuery{Key: org_model.OrgDomainSearchKeyOrgID, Method: global_model.SearchMethodEquals, Value: authz.GetCtxData(ctx).OrgID}) - sequence, sequenceErr := repo.View.GetLatestOrgDomainSequence() + sequence, sequenceErr := repo.View.GetLatestOrgDomainSequence("") logging.Log("EVENT-SLowp").OnError(sequenceErr).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest org domain sequence") domains, count, err := repo.View.SearchOrgDomains(request) if err != nil { @@ -205,7 +205,7 @@ func (repo *OrgRepository) RemoveMyOrgMember(ctx context.Context, userID string) func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.Queries[len(request.Queries)-1] = &org_model.OrgMemberSearchQuery{Key: org_model.OrgMemberSearchKeyOrgID, Method: global_model.SearchMethodEquals, Value: authz.GetCtxData(ctx).OrgID} - sequence, sequenceErr := repo.View.GetLatestOrgMemberSequence() + sequence, sequenceErr := repo.View.GetLatestOrgMemberSequence("") logging.Log("EVENT-Smu3d").OnError(sequenceErr).Warn("could not read latest org member sequence") members, count, err := repo.View.SearchOrgMembers(request) if err != nil { @@ -292,7 +292,7 @@ func (repo *OrgRepository) SearchIDPConfigs(ctx context.Context, request *iam_mo request.EnsureLimit(repo.SearchLimit) request.AppendMyOrgQuery(authz.GetCtxData(ctx).OrgID, repo.SystemDefaults.IamID) - sequence, sequenceErr := repo.View.GetLatestIDPConfigSequence() + sequence, sequenceErr := repo.View.GetLatestIDPConfigSequence("") logging.Log("EVENT-Dk8si").OnError(sequenceErr).Warn("could not read latest idp config sequence") idps, count, err := repo.View.SearchIDPConfigs(request) if err != nil { @@ -414,7 +414,7 @@ func (repo *OrgRepository) SearchIDPProviders(ctx context.Context, request *iam_ request.AppendAggregateIDQuery(authz.GetCtxData(ctx).OrgID) } request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestIDPProviderSequence() + sequence, sequenceErr := repo.View.GetLatestIDPProviderSequence("") logging.Log("EVENT-Tuiks").OnError(sequenceErr).Warn("could not read latest iam sequence") providers, count, err := repo.View.SearchIDPProviders(request) if err != nil { diff --git a/internal/management/repository/eventsourcing/eventstore/project.go b/internal/management/repository/eventsourcing/eventstore/project.go index 5a9343ff11..2d949a3f95 100644 --- a/internal/management/repository/eventsourcing/eventstore/project.go +++ b/internal/management/repository/eventsourcing/eventstore/project.go @@ -118,7 +118,7 @@ func (repo *ProjectRepo) RemoveProject(ctx context.Context, projectID string) er func (repo *ProjectRepo) SearchProjects(ctx context.Context, request *proj_model.ProjectViewSearchRequest) (*proj_model.ProjectViewSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectSequence() + sequence, sequenceErr := repo.View.GetLatestProjectSequence("") logging.Log("EVENT-Edc56").OnError(sequenceErr).Warn("could not read latest project sequence") permissions := authz.GetRequestPermissionsFromCtx(ctx) @@ -198,7 +198,7 @@ func (repo *ProjectRepo) RemoveProjectMember(ctx context.Context, projectID, use func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj_model.ProjectMemberSearchRequest) (*proj_model.ProjectMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectMemberSequence() + sequence, sequenceErr := repo.View.GetLatestProjectMemberSequence("") logging.Log("EVENT-3dgt6").OnError(sequenceErr).Warn("could not read latest project member sequence") members, count, err := repo.View.SearchProjectMembers(request) if err != nil { @@ -270,7 +270,7 @@ func (repo *ProjectRepo) RemoveProjectRole(ctx context.Context, projectID, key s func (repo *ProjectRepo) SearchProjectRoles(ctx context.Context, projectID string, request *proj_model.ProjectRoleSearchRequest) (*proj_model.ProjectRoleSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.AppendProjectQuery(projectID) - sequence, sequenceErr := repo.View.GetLatestProjectRoleSequence() + sequence, sequenceErr := repo.View.GetLatestProjectRoleSequence("") logging.Log("LSp0d-47suf").OnError(sequenceErr).Warn("could not read latest project role sequence") roles, count, err := repo.View.SearchProjectRoles(request) if err != nil { @@ -366,7 +366,7 @@ func (repo *ProjectRepo) RemoveApplication(ctx context.Context, projectID, appID func (repo *ProjectRepo) SearchApplications(ctx context.Context, request *proj_model.ApplicationSearchRequest) (*proj_model.ApplicationSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestApplicationSequence() + sequence, sequenceErr := repo.View.GetLatestApplicationSequence("") logging.Log("EVENT-SKe8s").OnError(sequenceErr).Warn("could not read latest application sequence") apps, count, err := repo.View.SearchApplications(request) if err != nil { @@ -423,7 +423,7 @@ func (repo *ProjectRepo) ProjectGrantByID(ctx context.Context, grantID string) ( func (repo *ProjectRepo) SearchProjectGrants(ctx context.Context, request *proj_model.ProjectGrantViewSearchRequest) (*proj_model.ProjectGrantViewSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence() + sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence("") logging.Log("EVENT-Skw9f").OnError(sequenceErr).Warn("could not read latest project grant sequence") projects, count, err := repo.View.SearchProjectGrants(request) if err != nil { @@ -444,7 +444,7 @@ func (repo *ProjectRepo) SearchProjectGrants(ctx context.Context, request *proj_ func (repo *ProjectRepo) SearchGrantedProjects(ctx context.Context, request *proj_model.ProjectGrantViewSearchRequest) (*proj_model.ProjectGrantViewSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence() + sequence, sequenceErr := repo.View.GetLatestProjectGrantSequence("") logging.Log("EVENT-Skw9f").OnError(sequenceErr).Warn("could not read latest project grant sequence") permissions := authz.GetRequestPermissionsFromCtx(ctx) @@ -612,7 +612,7 @@ func (repo *ProjectRepo) RemoveProjectGrantMember(ctx context.Context, projectID func (repo *ProjectRepo) SearchProjectGrantMembers(ctx context.Context, request *proj_model.ProjectGrantMemberSearchRequest) (*proj_model.ProjectGrantMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestProjectGrantMemberSequence() + sequence, sequenceErr := repo.View.GetLatestProjectGrantMemberSequence("") logging.Log("EVENT-Du8sk").OnError(sequenceErr).Warn("could not read latest project grant sequence") members, count, err := repo.View.SearchProjectGrantMembers(request) if err != nil { diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index a9c77c84b8..1c16407b4f 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -3,24 +3,22 @@ package eventstore import ( "context" - es_int "github.com/caos/zitadel/internal/eventstore" - es_models "github.com/caos/zitadel/internal/eventstore/models" - es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" - usr_grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing" - "github.com/caos/logging" - "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" + es_int "github.com/caos/zitadel/internal/eventstore" + es_models "github.com/caos/zitadel/internal/eventstore/models" + es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" global_model "github.com/caos/zitadel/internal/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" usr_model "github.com/caos/zitadel/internal/user/model" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" "github.com/caos/zitadel/internal/user/repository/view/model" + usr_grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing" "github.com/caos/zitadel/internal/view/repository" ) @@ -158,7 +156,7 @@ func (repo *UserRepo) RemoveUser(ctx context.Context, id string) error { func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSearchRequest) (*usr_model.UserSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestUserSequence() + sequence, sequenceErr := repo.View.GetLatestUserSequence("") logging.Log("EVENT-Lcn7d").OnError(sequenceErr).Warn("could not read latest user sequence") users, count, err := repo.View.SearchUsers(request) if err != nil { @@ -187,7 +185,7 @@ func (repo *UserRepo) UserChanges(ctx context.Context, id string, lastSequence u user, _ := repo.UserEvents.UserByID(ctx, change.ModifierID) if user != nil { if user.Human != nil { - change.ModifierName = user.DisplayName + change.ModifierName = user.Human.DisplayName } if user.Machine != nil { change.ModifierName = user.Machine.Name @@ -231,6 +229,18 @@ func (repo *UserRepo) RemoveOTP(ctx context.Context, userID string) error { return repo.UserEvents.RemoveOTP(ctx, userID) } +func (repo *UserRepo) RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error { + return repo.UserEvents.RemoveU2FToken(ctx, userID, webAuthNTokenID) +} + +func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*usr_model.WebAuthNToken, error) { + return repo.UserEvents.GetPasswordless(ctx, userID) +} + +func (repo *UserRepo) RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error { + return repo.UserEvents.RemovePasswordlessToken(ctx, userID, webAuthNTokenID) +} + func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) { policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) if err != nil && caos_errs.IsNotFound(err) { @@ -264,7 +274,7 @@ func (repo *UserRepo) ProfileByID(ctx context.Context, userID string) (*usr_mode func (repo *UserRepo) SearchExternalIDPs(ctx context.Context, request *usr_model.ExternalIDPSearchRequest) (*usr_model.ExternalIDPSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, seqErr := repo.View.GetLatestExternalIDPSequence() + sequence, seqErr := repo.View.GetLatestExternalIDPSequence("") logging.Log("EVENT-Qs7uf").OnError(seqErr).Warn("could not read latest external idp sequence") externalIDPS, count, err := repo.View.SearchExternalIDPs(request) if err != nil { @@ -301,7 +311,7 @@ func (repo *UserRepo) GetMachineKey(ctx context.Context, userID, keyID string) ( func (repo *UserRepo) SearchMachineKeys(ctx context.Context, request *usr_model.MachineKeySearchRequest) (*usr_model.MachineKeySearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, seqErr := repo.View.GetLatestMachineKeySequence() + sequence, seqErr := repo.View.GetLatestMachineKeySequence("") logging.Log("EVENT-Sk8fs").OnError(seqErr).Warn("could not read latest user sequence") keys, count, err := repo.View.SearchMachineKeys(request) if err != nil { @@ -403,7 +413,7 @@ func (repo *UserRepo) ChangeAddress(ctx context.Context, address *usr_model.Addr func (repo *UserRepo) SearchUserMemberships(ctx context.Context, request *usr_model.UserMembershipSearchRequest) (*usr_model.UserMembershipSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestUserMembershipSequence() + sequence, sequenceErr := repo.View.GetLatestUserMembershipSequence("") logging.Log("EVENT-Dn7sf").OnError(sequenceErr).Warn("could not read latest user sequence") result := handleSearchUserMembershipsPermissions(ctx, request, sequence) diff --git a/internal/management/repository/eventsourcing/eventstore/user_grant.go b/internal/management/repository/eventsourcing/eventstore/user_grant.go index 09568b64fc..d2f4f337e9 100644 --- a/internal/management/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/management/repository/eventsourcing/eventstore/user_grant.go @@ -116,7 +116,7 @@ func (repo *UserGrantRepo) BulkRemoveUserGrant(ctx context.Context, grantIDs ... func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - sequence, sequenceErr := repo.View.GetLatestUserGrantSequence() + sequence, sequenceErr := repo.View.GetLatestUserGrantSequence("") logging.Log("EVENT-5Viwf").OnError(sequenceErr).Warn("could not read latest user grant sequence") result := handleSearchUserGrantPermissions(ctx, request, sequence) diff --git a/internal/management/repository/eventsourcing/handler/application.go b/internal/management/repository/eventsourcing/handler/application.go index 8d1490c433..442eae59b3 100644 --- a/internal/management/repository/eventsourcing/handler/application.go +++ b/internal/management/repository/eventsourcing/handler/application.go @@ -5,7 +5,9 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -13,21 +15,57 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Application struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( applicationTable = "management.applications" ) +type Application struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newApplication( + handler handler, + projectEvents *proj_event.ProjectEventstore, +) *Application { + h := &Application{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (a *Application) subscribe() { + a.subscription = a.es.Subscribe(a.AggregateTypes()...) + go func() { + for event := range a.subscription.Events { + query.ReduceEvent(a, event) + } + }() +} + func (a *Application) ViewModel() string { return applicationTable } +func (_ *Application) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (a *Application) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := a.view.GetLatestApplicationSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (a *Application) EventQuery() (*models.SearchQuery, error) { - sequence, err := a.view.GetLatestApplicationSequence() + sequence, err := a.view.GetLatestApplicationSequence("") if err != nil { return nil, err } @@ -65,30 +103,30 @@ func (a *Application) Reduce(event *models.Event) (err error) { if err != nil { return err } - return a.view.DeleteApplication(app.ID, event.Sequence, event.CreationDate) + return a.view.DeleteApplication(app.ID, event) case es_model.ProjectChanged: apps, err := a.view.ApplicationsByProjectID(event.AggregateID) if err != nil { return err } if len(apps) == 0 { - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } for _, app := range apps { if err := app.AppendEvent(event); err != nil { return err } } - return a.view.PutApplications(apps, event.Sequence, event.CreationDate) + return a.view.PutApplications(apps, event) case es_model.ProjectRemoved: return a.view.DeleteApplicationsByProjectID(event.AggregateID) default: - return a.view.ProcessedApplicationSequence(event.Sequence, event.CreationDate) + return a.view.ProcessedApplicationSequence(event) } if err != nil { return err } - return a.view.PutApplication(app, event.CreationDate) + return a.view.PutApplication(app, event) } func (a *Application) OnError(event *models.Event, spoolerError error) error { diff --git a/internal/management/repository/eventsourcing/handler/handler.go b/internal/management/repository/eventsourcing/handler/handler.go index 531a9db468..addd360caf 100644 --- a/internal/management/repository/eventsourcing/handler/handler.go +++ b/internal/management/repository/eventsourcing/handler/handler.go @@ -25,6 +25,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -34,49 +40,76 @@ type EventstoreRepos struct { IamEvents *iam_event.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, defaults systemdefaults.SystemDefaults) []query.Handler { return []query.Handler{ - &Project{handler: handler{view, bulkLimit, configs.cycleDuration("Project"), errorCount}, - eventstore: eventstore}, - &ProjectGrant{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrant"), errorCount}, - eventstore: eventstore, projectEvents: repos.ProjectEvents, orgEvents: repos.OrgEvents}, - &ProjectRole{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount}, - projectEvents: repos.ProjectEvents}, - &ProjectMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount}, - userEvents: repos.UserEvents}, - &ProjectGrantMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount}, - userEvents: repos.UserEvents}, - &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, - projectEvents: repos.ProjectEvents}, - &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - eventstore: eventstore, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents, iamID: defaults.IamID}, - &UserGrant{handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, - projectEvents: repos.ProjectEvents, userEvents: repos.UserEvents, orgEvents: repos.OrgEvents}, - &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, - &OrgMember{handler: handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount}, - userEvents: repos.UserEvents}, - &OrgDomain{handler: handler{view, bulkLimit, configs.cycleDuration("OrgDomain"), errorCount}}, - &UserMembership{handler: handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount}, - orgEvents: repos.OrgEvents, projectEvents: repos.ProjectEvents}, - &MachineKeys{handler: handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount}}, - &IDPConfig{handler: handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount}}, - &LoginPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount}}, - &LabelPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount}}, - &IDPProvider{handler: handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount}, - systemDefaults: defaults, iamEvents: repos.IamEvents, orgEvents: repos.OrgEvents}, - &ExternalIDP{handler: handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount}, - systemDefaults: defaults, iamEvents: repos.IamEvents, orgEvents: repos.OrgEvents}, - &PasswordComplexityPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount}}, - &PasswordAgePolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount}}, - &PasswordLockoutPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount}}, - &OrgIAMPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount}}, + newProject( + handler{view, bulkLimit, configs.cycleDuration("Project"), errorCount, es}), + newProjectGrant( + handler{view, bulkLimit, configs.cycleDuration("ProjectGrant"), errorCount, es}, + repos.ProjectEvents, + repos.OrgEvents), + newProjectRole(handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount, es}, + repos.ProjectEvents), + newProjectMember(handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount, es}, + repos.UserEvents), + newProjectGrantMember(handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount, es}, + repos.UserEvents), + newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}, + repos.ProjectEvents), + newUser(handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IamEvents, + defaults.IamID), + newUserGrant(handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, + repos.ProjectEvents, + repos.UserEvents, + repos.OrgEvents), + newOrg( + handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}), + newOrgMember( + handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}, + repos.UserEvents), + newOrgDomain( + handler{view, bulkLimit, configs.cycleDuration("OrgDomain"), errorCount, es}), + newUserMembership( + handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}, + repos.OrgEvents, + repos.ProjectEvents), + newMachineKeys( + handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount, es}), + newIDPConfig( + handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), + newLoginPolicy( + handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount, es}), + newLabelPolicy( + handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}), + newIDPProvider( + handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, + + defaults, + repos.IamEvents, + repos.OrgEvents), + newExternalIDP( + handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount, es}, + + defaults, + repos.IamEvents, + repos.OrgEvents), + newPasswordComplexityPolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount, es}), + newPasswordAgePolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordAgePolicy"), errorCount, es}), + newPasswordLockoutPolicy( + handler{view, bulkLimit, configs.cycleDuration("PasswordLockoutPolicy"), errorCount, es}), + newOrgIAMPolicy( + handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount, es}), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } @@ -85,6 +118,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/management/repository/eventsourcing/handler/idp_config.go b/internal/management/repository/eventsourcing/handler/idp_config.go index 07301c71b8..5fa73038e5 100644 --- a/internal/management/repository/eventsourcing/handler/idp_config.go +++ b/internal/management/repository/eventsourcing/handler/idp_config.go @@ -2,8 +2,10 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" @@ -11,29 +13,61 @@ import ( "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type IDPConfig struct { - handler -} - const ( idpConfigTable = "management.idp_configs" ) +type IDPConfig struct { + handler + subscription *eventstore.Subscription +} + +func newIDPConfig(handler handler) *IDPConfig { + h := &IDPConfig{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *IDPConfig) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *IDPConfig) ViewModel() string { return idpConfigTable } -func (m *IDPConfig) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestIDPConfigSequence() +func (_ *IDPConfig) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (m *IDPConfig) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestIDPConfigSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *IDPConfig) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestIDPConfigSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *IDPConfig) Reduce(event *models.Event) (err error) { +func (m *IDPConfig) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.OrgAggregate: err = m.processIdpConfig(iam_model.IDPProviderTypeOrg, event) @@ -43,7 +77,7 @@ func (m *IDPConfig) Reduce(event *models.Event) (err error) { return err } -func (m *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, event *models.Event) (err error) { +func (m *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, event *es_models.Event) (err error) { idp := new(iam_view_model.IDPConfigView) switch event.Type { case model.IDPConfigAdded, @@ -66,17 +100,17 @@ func (m *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, eve if err != nil { return err } - return m.view.DeleteIDPConfig(idp.IDPConfigID, event.Sequence, event.CreationDate) + return m.view.DeleteIDPConfig(idp.IDPConfigID, event) default: - return m.view.ProcessedIDPConfigSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIDPConfigSequence(event) } if err != nil { return err } - return m.view.PutIDPConfig(idp, idp.Sequence, event.CreationDate) + return m.view.PutIDPConfig(idp, event) } -func (i *IDPConfig) OnError(event *models.Event, err error) error { +func (i *IDPConfig) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Nxu8s", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp config handler") return spooler.HandleError(event, err, i.view.GetLatestIDPConfigFailedEvent, i.view.ProcessedIDPConfigFailedEvent, i.view.ProcessedIDPConfigSequence, i.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/idp_providers.go b/internal/management/repository/eventsourcing/handler/idp_providers.go index 9d873eed67..1b5933917b 100644 --- a/internal/management/repository/eventsourcing/handler/idp_providers.go +++ b/internal/management/repository/eventsourcing/handler/idp_providers.go @@ -2,18 +2,23 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" - "github.com/caos/zitadel/internal/iam/repository/eventsourcing" - org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" - org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" - - "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" + org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" +) + +const ( + idpProviderTable = "management.idp_providers" ) type IDPProvider struct { @@ -21,27 +26,63 @@ type IDPProvider struct { systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - idpProviderTable = "management.idp_providers" -) +func newIDPProvider( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *IDPProvider { + h := &IDPProvider{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *IDPProvider) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (m *IDPProvider) ViewModel() string { return idpProviderTable } -func (m *IDPProvider) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestIDPProviderSequence() +func (_ *IDPProvider) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (m *IDPProvider) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := m.view.GetLatestIDPProviderSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *IDPProvider) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestIDPProviderSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *IDPProvider) Reduce(event *models.Event) (err error) { +func (m *IDPProvider) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.IAMAggregate, org_es_model.OrgAggregate: err = m.processIdpProvider(event) @@ -49,7 +90,7 @@ func (m *IDPProvider) Reduce(event *models.Event) (err error) { return err } -func (m *IDPProvider) processIdpProvider(event *models.Event) (err error) { +func (m *IDPProvider) processIdpProvider(event *es_models.Event) (err error) { provider := new(iam_view_model.IDPProviderView) switch event.Type { case model.LoginPolicyIDPProviderAdded, org_es_model.LoginPolicyIDPProviderAdded: @@ -64,7 +105,7 @@ func (m *IDPProvider) processIdpProvider(event *models.Event) (err error) { if err != nil { return err } - return m.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event.Sequence, event.CreationDate) + return m.view.DeleteIDPProvider(event.AggregateID, provider.IDPConfigID, event) case model.IDPConfigChanged, org_es_model.IDPConfigChanged: esConfig := new(iam_view_model.IDPConfigView) providerType := iam_model.IDPProviderTypeSystem @@ -88,16 +129,16 @@ func (m *IDPProvider) processIdpProvider(event *models.Event) (err error) { for _, provider := range providers { m.fillConfigData(provider, config) } - return m.view.PutIDPProviders(event.Sequence, event.CreationDate, providers...) + return m.view.PutIDPProviders(event, providers...) case org_es_model.LoginPolicyRemoved: - return m.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteIDPProvidersByAggregateID(event.AggregateID, event) default: - return m.view.ProcessedIDPProviderSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedIDPProviderSequence(event) } if err != nil { return err } - return m.view.PutIDPProvider(provider, provider.Sequence, event.CreationDate) + return m.view.PutIDPProvider(provider, event) } func (m *IDPProvider) fillData(provider *iam_view_model.IDPProviderView) (err error) { @@ -121,7 +162,7 @@ func (m *IDPProvider) fillConfigData(provider *iam_view_model.IDPProviderView, c provider.IDPState = int32(config.State) } -func (m *IDPProvider) OnError(event *models.Event, err error) error { +func (m *IDPProvider) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Msj8c", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp provider handler") return spooler.HandleError(event, err, m.view.GetLatestIDPProviderFailedEvent, m.view.ProcessedIDPProviderFailedEvent, m.view.ProcessedIDPProviderSequence, m.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/label_policy.go b/internal/management/repository/eventsourcing/handler/label_policy.go index 2c47a03c80..2cc3ba833e 100644 --- a/internal/management/repository/eventsourcing/handler/label_policy.go +++ b/internal/management/repository/eventsourcing/handler/label_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type LabelPolicy struct { - handler -} - const ( labelPolicyTable = "management.label_policies" ) +type LabelPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLabelPolicy(handler handler) *LabelPolicy { + h := &LabelPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *LabelPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *LabelPolicy) ViewModel() string { return labelPolicyTable } +func (_ *LabelPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (m *LabelPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestLabelPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *LabelPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestLabelPolicySequence() + sequence, err := m.view.GetLatestLabelPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,12 +86,12 @@ func (m *LabelPolicy) processLabelPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) default: - return m.view.ProcessedLabelPolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedLabelPolicySequence(event) } if err != nil { return err } - return m.view.PutLabelPolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutLabelPolicy(policy, event) } func (m *LabelPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/login_policy.go b/internal/management/repository/eventsourcing/handler/login_policy.go index cb6f06c4d2..cf8994ff1e 100644 --- a/internal/management/repository/eventsourcing/handler/login_policy.go +++ b/internal/management/repository/eventsourcing/handler/login_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type LoginPolicy struct { - handler -} - const ( loginPolicyTable = "management.login_policies" ) +type LoginPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newLoginPolicy(handler handler) *LoginPolicy { + h := &LoginPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *LoginPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *LoginPolicy) ViewModel() string { return loginPolicyTable } +func (_ *LoginPolicy) AggregateTypes() []models.AggregateType { + return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (m *LoginPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := m.view.GetLatestLoginPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *LoginPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestLoginPolicySequence() + sequence, err := m.view.GetLatestLoginPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -57,14 +90,14 @@ func (m *LoginPolicy) processLoginPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.LoginPolicyRemoved: - return m.view.DeleteLoginPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteLoginPolicy(event.AggregateID, event) default: - return m.view.ProcessedLoginPolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedLoginPolicySequence(event) } if err != nil { return err } - return m.view.PutLoginPolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutLoginPolicy(policy, event) } func (m *LoginPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/machine_keys.go b/internal/management/repository/eventsourcing/handler/machine_keys.go index cd6b29b6a8..6758c4f173 100644 --- a/internal/management/repository/eventsourcing/handler/machine_keys.go +++ b/internal/management/repository/eventsourcing/handler/machine_keys.go @@ -4,33 +4,66 @@ import ( "time" "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type MachineKeys struct { - handler -} - const ( machineKeysTable = "management.machine_keys" ) +type MachineKeys struct { + handler + subscription *eventstore.Subscription +} + +func newMachineKeys(handler handler) *MachineKeys { + h := &MachineKeys{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *MachineKeys) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (d *MachineKeys) ViewModel() string { return machineKeysTable } +func (_ *MachineKeys) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate} +} + +func (k *MachineKeys) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := k.view.GetLatestMachineKeySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (d *MachineKeys) EventQuery() (*models.SearchQuery, error) { - sequence, err := d.view.GetLatestMachineKeySequence() + sequence, err := d.view.GetLatestMachineKeySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate). + AggregateTypeFilter(d.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -48,23 +81,23 @@ func (d *MachineKeys) processMachineKeys(event *models.Event) (err error) { case model.MachineKeyAdded: err = key.AppendEvent(event) if key.ExpirationDate.Before(time.Now()) { - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return d.view.ProcessedMachineKeySequence(event) } case model.MachineKeyRemoved: err = key.SetData(event) if err != nil { return err } - return d.view.DeleteMachineKey(key.ID, event.Sequence, event.CreationDate) + return d.view.DeleteMachineKey(key.ID, event) case model.UserRemoved: - return d.view.DeleteMachineKeysByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return d.view.DeleteMachineKeysByUserID(event.AggregateID, event) default: - return d.view.ProcessedMachineKeySequence(event.Sequence, event.CreationDate) + return d.view.ProcessedMachineKeySequence(event) } if err != nil { return err } - return d.view.PutMachineKey(key, key.Sequence, event.CreationDate) + return d.view.PutMachineKey(key, event) } func (d *MachineKeys) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/org.go b/internal/management/repository/eventsourcing/handler/org.go index 23155a5c52..a4f6eb211d 100644 --- a/internal/management/repository/eventsourcing/handler/org.go +++ b/internal/management/repository/eventsourcing/handler/org.go @@ -2,28 +2,62 @@ package handler import ( "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type Org struct { - handler -} - const ( orgTable = "management.orgs" ) +type Org struct { + handler + subscription *eventstore.Subscription +} + +func newOrg(handler handler) *Org { + h := &Org{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *Org) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (o *Org) ViewModel() string { return orgTable } +func (_ *Org) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (o *Org) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestOrgSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (o *Org) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := o.view.GetLatestOrgSequence() + sequence, err := o.view.GetLatestOrgSequence("") if err != nil { return nil, err } @@ -47,12 +81,12 @@ func (o *Org) Reduce(event *es_models.Event) (err error) { } err = org.AppendEvent(event) default: - return o.view.ProcessedOrgSequence(event.Sequence, event.CreationDate) + return o.view.ProcessedOrgSequence(event) } if err != nil { return err } - return o.view.PutOrg(org, event.CreationDate) + return o.view.PutOrg(org, event) } func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { diff --git a/internal/management/repository/eventsourcing/handler/org_domain.go b/internal/management/repository/eventsourcing/handler/org_domain.go index 9dc6307749..9a7dedb5ee 100644 --- a/internal/management/repository/eventsourcing/handler/org_domain.go +++ b/internal/management/repository/eventsourcing/handler/org_domain.go @@ -2,33 +2,66 @@ package handler import ( "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" ) -type OrgDomain struct { - handler -} - const ( orgDomainTable = "management.org_domains" ) +type OrgDomain struct { + handler + subscription *eventstore.Subscription +} + +func newOrgDomain(handler handler) *OrgDomain { + h := &OrgDomain{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *OrgDomain) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (d *OrgDomain) ViewModel() string { return orgDomainTable } +func (_ *OrgDomain) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate} +} + +func (p *OrgDomain) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgDomainSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (d *OrgDomain) EventQuery() (*models.SearchQuery, error) { - sequence, err := d.view.GetLatestOrgDomainSequence() + sequence, err := d.view.GetLatestOrgDomainSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate). + AggregateTypeFilter(d.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,7 +105,7 @@ func (d *OrgDomain) processOrgDomain(event *models.Event) (err error) { for _, existingDomain := range existingDomains { existingDomain.Primary = false } - err = d.view.PutOrgDomains(existingDomains, 0, event.CreationDate) + err = d.view.PutOrgDomains(existingDomains, event) if err != nil { return err } @@ -82,14 +115,14 @@ func (d *OrgDomain) processOrgDomain(event *models.Event) (err error) { if err != nil { return err } - return d.view.DeleteOrgDomain(event.AggregateID, domain.Domain, event.Sequence, event.CreationDate) + return d.view.DeleteOrgDomain(event.AggregateID, domain.Domain, event) default: - return d.view.ProcessedOrgDomainSequence(event.Sequence, event.CreationDate) + return d.view.ProcessedOrgDomainSequence(event) } if err != nil { return err } - return d.view.PutOrgDomain(domain, domain.Sequence, event.CreationDate) + return d.view.PutOrgDomain(domain, event) } func (d *OrgDomain) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/org_iam_policy.go b/internal/management/repository/eventsourcing/handler/org_iam_policy.go index fffe08cfec..796652bbb4 100644 --- a/internal/management/repository/eventsourcing/handler/org_iam_policy.go +++ b/internal/management/repository/eventsourcing/handler/org_iam_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type OrgIAMPolicy struct { - handler -} - const ( orgIAMPolicyTable = "management.org_iam_policies" ) +type OrgIAMPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newOrgIAMPolicy(handler handler) *OrgIAMPolicy { + h := &OrgIAMPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *OrgIAMPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *OrgIAMPolicy) ViewModel() string { return orgIAMPolicyTable } +func (_ *OrgIAMPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *OrgIAMPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgIAMPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *OrgIAMPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestOrgIAMPolicySequence() + sequence, err := m.view.GetLatestOrgIAMPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +86,14 @@ func (m *OrgIAMPolicy) processOrgIAMPolicy(event *models.Event) (err error) { } err = policy.AppendEvent(event) case model.OrgIAMPolicyRemoved: - return m.view.DeleteOrgIAMPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteOrgIAMPolicy(event.AggregateID, event) default: - return m.view.ProcessedOrgIAMPolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgIAMPolicySequence(event) } if err != nil { return err } - return m.view.PutOrgIAMPolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutOrgIAMPolicy(policy, event) } func (m *OrgIAMPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/org_member.go b/internal/management/repository/eventsourcing/handler/org_member.go index 26f2b2fb86..cf3e1b6499 100644 --- a/internal/management/repository/eventsourcing/handler/org_member.go +++ b/internal/management/repository/eventsourcing/handler/org_member.go @@ -4,9 +4,10 @@ import ( "context" "github.com/caos/logging" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/repository/view/model" @@ -15,26 +16,62 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type OrgMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( orgMemberTable = "management.org_members" ) +type OrgMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newOrgMember( + handler handler, + userEvents *usr_event.UserEventstore, +) *OrgMember { + h := &OrgMember{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (m *OrgMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *OrgMember) ViewModel() string { return orgMemberTable } +func (_ *OrgMember) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, usr_es_model.UserAggregate} +} + +func (p *OrgMember) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestOrgMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (m *OrgMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestOrgMemberSequence() + sequence, err := m.view.GetLatestOrgMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,14 +109,14 @@ func (m *OrgMember) processOrgMember(event *models.Event) (err error) { if err != nil { return err } - return m.view.DeleteOrgMember(event.AggregateID, member.UserID, event.Sequence, event.CreationDate) + return m.view.DeleteOrgMember(event.AggregateID, member.UserID, event) default: - return m.view.ProcessedOrgMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgMemberSequence(event) } if err != nil { return err } - return m.view.PutOrgMember(member, member.Sequence, event.CreationDate) + return m.view.PutOrgMember(member, event) } func (m *OrgMember) processUser(event *models.Event) (err error) { @@ -94,7 +131,7 @@ func (m *OrgMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return m.view.ProcessedOrgMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgMemberSequence(event) } user, err := m.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -103,13 +140,12 @@ func (m *OrgMember) processUser(event *models.Event) (err error) { for _, member := range members { m.fillUserData(member, user) } - return m.view.PutOrgMembers(members, event.Sequence, event.CreationDate) + return m.view.PutOrgMembers(members, event) case usr_es_model.UserRemoved: - return m.view.DeleteOrgMembersByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteOrgMembersByUserID(event.AggregateID, event) default: - return m.view.ProcessedOrgMemberSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedOrgMemberSequence(event) } - return nil } func (m *OrgMember) fillData(member *org_model.OrgMemberView) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/password_age_policy.go b/internal/management/repository/eventsourcing/handler/password_age_policy.go index 535acf03db..eee2e2711c 100644 --- a/internal/management/repository/eventsourcing/handler/password_age_policy.go +++ b/internal/management/repository/eventsourcing/handler/password_age_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordAgePolicy struct { - handler -} - const ( passwordAgePolicyTable = "management.password_age_policies" ) +type PasswordAgePolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordAgePolicy(handler handler) *PasswordAgePolicy { + h := &PasswordAgePolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *PasswordAgePolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (m *PasswordAgePolicy) ViewModel() string { return passwordAgePolicyTable } -func (m *PasswordAgePolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestPasswordAgePolicySequence() +func (_ *PasswordAgePolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (o *PasswordAgePolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := o.view.GetLatestPasswordAgePolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *PasswordAgePolicy) EventQuery() (*models.SearchQuery, error) { + sequence, err := p.view.GetLatestPasswordAgePolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +86,14 @@ func (m *PasswordAgePolicy) processPasswordAgePolicy(event *models.Event) (err e } err = policy.AppendEvent(event) case model.PasswordAgePolicyRemoved: - return m.view.DeletePasswordAgePolicy(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeletePasswordAgePolicy(event.AggregateID, event) default: - return m.view.ProcessedPasswordAgePolicySequence(event.Sequence, event.CreationDate) + return m.view.ProcessedPasswordAgePolicySequence(event) } if err != nil { return err } - return m.view.PutPasswordAgePolicy(policy, policy.Sequence, event.CreationDate) + return m.view.PutPasswordAgePolicy(policy, event) } func (m *PasswordAgePolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/password_complexity_policy.go b/internal/management/repository/eventsourcing/handler/password_complexity_policy.go index 4ada8b0c95..d2d14c885d 100644 --- a/internal/management/repository/eventsourcing/handler/password_complexity_policy.go +++ b/internal/management/repository/eventsourcing/handler/password_complexity_policy.go @@ -2,34 +2,67 @@ package handler import ( "github.com/caos/logging" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordComplexityPolicy struct { - handler -} - const ( passwordComplexityPolicyTable = "management.password_complexity_policies" ) +type PasswordComplexityPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordComplexityPolicy(handler handler) *PasswordComplexityPolicy { + h := &PasswordComplexityPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *PasswordComplexityPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *PasswordComplexityPolicy) ViewModel() string { return passwordComplexityPolicyTable } +func (_ *PasswordComplexityPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordComplexityPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordComplexityPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordComplexityPolicySequence() + sequence, err := p.view.GetLatestPasswordComplexityPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +86,14 @@ func (p *PasswordComplexityPolicy) processPasswordComplexityPolicy(event *models } err = policy.AppendEvent(event) case model.PasswordComplexityPolicyRemoved: - return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordComplexityPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordComplexityPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordComplexityPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordComplexityPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordComplexityPolicy(policy, event) } func (p *PasswordComplexityPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/password_lockout_policy.go b/internal/management/repository/eventsourcing/handler/password_lockout_policy.go index c5a66d9528..19e7c9c0b6 100644 --- a/internal/management/repository/eventsourcing/handler/password_lockout_policy.go +++ b/internal/management/repository/eventsourcing/handler/password_lockout_policy.go @@ -2,34 +2,68 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) -type PasswordLockoutPolicy struct { - handler -} - const ( passwordLockoutPolicyTable = "management.password_lockout_policies" ) +type PasswordLockoutPolicy struct { + handler + subscription *eventstore.Subscription +} + +func newPasswordLockoutPolicy(handler handler) *PasswordLockoutPolicy { + h := &PasswordLockoutPolicy{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *PasswordLockoutPolicy) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *PasswordLockoutPolicy) ViewModel() string { return passwordLockoutPolicyTable } +func (_ *PasswordLockoutPolicy) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} +} + +func (p *PasswordLockoutPolicy) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *PasswordLockoutPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestPasswordLockoutPolicySequence() + sequence, err := p.view.GetLatestPasswordLockoutPolicySequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.OrgAggregate, iam_es_model.IAMAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -53,14 +87,14 @@ func (p *PasswordLockoutPolicy) processPasswordLockoutPolicy(event *models.Event } err = policy.AppendEvent(event) case model.PasswordLockoutPolicyRemoved: - return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeletePasswordLockoutPolicy(event.AggregateID, event) default: - return p.view.ProcessedPasswordLockoutPolicySequence(event.Sequence, event.CreationDate) + return p.view.ProcessedPasswordLockoutPolicySequence(event) } if err != nil { return err } - return p.view.PutPasswordLockoutPolicy(policy, policy.Sequence, event.CreationDate) + return p.view.PutPasswordLockoutPolicy(policy, event) } func (p *PasswordLockoutPolicy) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/project.go b/internal/management/repository/eventsourcing/handler/project.go index 0536de9ae5..51331d44ee 100644 --- a/internal/management/repository/eventsourcing/handler/project.go +++ b/internal/management/repository/eventsourcing/handler/project.go @@ -5,27 +5,59 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type Project struct { - handler - eventstore eventstore.Eventstore -} - const ( projectTable = "management.projects" ) +type Project struct { + handler + subscription *eventstore.Subscription +} + +func newProject(handler handler) *Project { + h := &Project{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (m *Project) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *Project) ViewModel() string { return projectTable } +func (_ *Project) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (p *Project) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *Project) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectSequence() + sequence, err := p.view.GetLatestProjectSequence("") if err != nil { return nil, err } @@ -46,14 +78,14 @@ func (p *Project) Reduce(event *models.Event) (err error) { } err = project.AppendEvent(event) case es_model.ProjectRemoved: - return p.view.DeleteProject(event.AggregateID, event.Sequence, event.CreationDate) + return p.view.DeleteProject(event.AggregateID, event) default: - return p.view.ProcessedProjectSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectSequence(event) } if err != nil { return err } - return p.view.PutProject(project, event.CreationDate) + return p.view.PutProject(project, event) } func (p *Project) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/project_grant.go b/internal/management/repository/eventsourcing/handler/project_grant.go index 1c7306bb21..918eb54f56 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant.go +++ b/internal/management/repository/eventsourcing/handler/project_grant.go @@ -2,12 +2,12 @@ package handler import ( "context" - "time" "github.com/caos/logging" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" org_model "github.com/caos/zitadel/internal/org/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" @@ -17,23 +17,60 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type ProjectGrant struct { - handler - eventstore eventstore.Eventstore - projectEvents *proj_event.ProjectEventstore - orgEvents *org_event.OrgEventstore -} - const ( grantedProjectTable = "management.project_grants" ) +type ProjectGrant struct { + handler + projectEvents *proj_event.ProjectEventstore + orgEvents *org_event.OrgEventstore + subscription *eventstore.Subscription +} + +func newProjectGrant( + handler handler, + projectEvents *proj_event.ProjectEventstore, + orgEvents *org_event.OrgEventstore, +) *ProjectGrant { + h := &ProjectGrant{ + handler: handler, + projectEvents: projectEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectGrant) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectGrant) ViewModel() string { return grantedProjectTable } +func (_ *ProjectGrant) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (p *ProjectGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectGrant) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectGrantSequence() + sequence, err := p.view.GetLatestProjectGrantSequence("") if err != nil { return nil, err } @@ -48,7 +85,7 @@ func (p *ProjectGrant) Reduce(event *models.Event) (err error) { if err != nil { return err } - return p.updateExistingProjects(project, event.Sequence, event.CreationDate) + return p.updateExistingProjects(project, event) case es_model.ProjectGrantAdded: err = grantedProject.AppendEvent(event) if err != nil { @@ -86,16 +123,20 @@ func (p *ProjectGrant) Reduce(event *models.Event) (err error) { if err != nil { return err } - return p.view.DeleteProjectGrant(grant.GrantID, event.Sequence, event.CreationDate) + return p.view.DeleteProjectGrant(grant.GrantID, event) case es_model.ProjectRemoved: - return p.view.DeleteProjectGrantsByProjectID(event.AggregateID) + err = p.view.DeleteProjectGrantsByProjectID(event.AggregateID) + if err != nil { + return err + } + return p.view.ProcessedProjectGrantSequence(event) default: - return p.view.ProcessedProjectGrantSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantSequence(event) } if err != nil { return err } - return p.view.PutProjectGrant(grantedProject, event.CreationDate) + return p.view.PutProjectGrant(grantedProject, event) } func (p *ProjectGrant) fillOrgData(grantedProject *view_model.ProjectGrantView, org, resourceOwner *org_model.Org) { @@ -107,7 +148,7 @@ func (p *ProjectGrant) getProject(projectID string) (*proj_model.Project, error) return p.projectEvents.ProjectByID(context.Background(), projectID) } -func (p *ProjectGrant) updateExistingProjects(project *view_model.ProjectView, sequence uint64, eventTimestamp time.Time) error { +func (p *ProjectGrant) updateExistingProjects(project *view_model.ProjectView, event *models.Event) error { projectGrants, err := p.view.ProjectGrantsByProjectID(project.ProjectID) if err != nil { logging.LogWithFields("SPOOL-los03", "id", project.ProjectID).WithError(err).Warn("could not update existing projects") @@ -115,7 +156,7 @@ func (p *ProjectGrant) updateExistingProjects(project *view_model.ProjectView, s for _, existingGrant := range projectGrants { existingGrant.Name = project.Name } - return p.view.PutProjectGrants(projectGrants, sequence, eventTimestamp) + return p.view.PutProjectGrants(projectGrants, event) } func (p *ProjectGrant) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/project_grant_member.go b/internal/management/repository/eventsourcing/handler/project_grant_member.go index fb37af782e..159be6d870 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant_member.go +++ b/internal/management/repository/eventsourcing/handler/project_grant_member.go @@ -5,8 +5,10 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" @@ -15,26 +17,62 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type ProjectGrantMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( projectGrantMemberTable = "management.project_grant_members" ) +type ProjectGrantMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newProjectGrantMember( + handler handler, + userEvents *usr_event.UserEventstore, +) *ProjectGrantMember { + h := &ProjectGrantMember{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectGrantMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectGrantMember) ViewModel() string { return projectGrantMemberTable } +func (_ *ProjectGrantMember) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{proj_es_model.ProjectAggregate, usr_es_model.UserAggregate} +} + +func (p *ProjectGrantMember) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectGrantMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectGrantMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectGrantMemberSequence() + sequence, err := p.view.GetLatestProjectGrantMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(proj_es_model.ProjectAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,16 +110,20 @@ func (p *ProjectGrantMember) processProjectGrantMember(event *models.Event) (err if err != nil { return err } - return p.view.DeleteProjectGrantMember(member.GrantID, member.UserID, event.Sequence, event.CreationDate) + return p.view.DeleteProjectGrantMember(member.GrantID, member.UserID, event) case proj_es_model.ProjectRemoved: - return p.view.DeleteProjectGrantMembersByProjectID(event.AggregateID) + err = p.view.DeleteProjectGrantMembersByProjectID(event.AggregateID) + if err != nil { + return err + } + return p.view.ProcessedProjectGrantMemberSequence(event) default: - return p.view.ProcessedProjectGrantMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantMemberSequence(event) } if err != nil { return err } - return p.view.PutProjectGrantMember(member, member.Sequence, event.CreationDate) + return p.view.PutProjectGrantMember(member, event) } func (p *ProjectGrantMember) processUser(event *models.Event) (err error) { @@ -96,7 +138,7 @@ func (p *ProjectGrantMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return p.view.ProcessedProjectGrantMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantMemberSequence(event) } user, err := p.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -105,11 +147,10 @@ func (p *ProjectGrantMember) processUser(event *models.Event) (err error) { for _, member := range members { p.fillUserData(member, user) } - return p.view.PutProjectGrantMembers(members, event.Sequence, event.CreationDate) + return p.view.PutProjectGrantMembers(members, event) default: - return p.view.ProcessedProjectGrantMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectGrantMemberSequence(event) } - return nil } func (p *ProjectGrantMember) fillData(member *view_model.ProjectGrantMemberView) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/project_member.go b/internal/management/repository/eventsourcing/handler/project_member.go index 7de3603f25..721f42e9d8 100644 --- a/internal/management/repository/eventsourcing/handler/project_member.go +++ b/internal/management/repository/eventsourcing/handler/project_member.go @@ -5,8 +5,10 @@ import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" @@ -15,26 +17,62 @@ import ( usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type ProjectMember struct { - handler - userEvents *usr_event.UserEventstore -} - const ( projectMemberTable = "management.project_members" ) +type ProjectMember struct { + handler + userEvents *usr_event.UserEventstore + subscription *eventstore.Subscription +} + +func newProjectMember( + handler handler, + userEvents *usr_event.UserEventstore, +) *ProjectMember { + h := &ProjectMember{ + handler: handler, + userEvents: userEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectMember) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectMember) ViewModel() string { return projectMemberTable } +func (_ *ProjectMember) AggregateTypes() []models.AggregateType { + return []models.AggregateType{proj_es_model.ProjectAggregate, usr_es_model.UserAggregate} +} + +func (p *ProjectMember) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectMemberSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectMember) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectMemberSequence() + sequence, err := p.view.GetLatestProjectMemberSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(proj_es_model.ProjectAggregate, usr_es_model.UserAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -72,16 +110,16 @@ func (p *ProjectMember) processProjectMember(event *models.Event) (err error) { if err != nil { return err } - return p.view.DeleteProjectMember(event.AggregateID, member.UserID, event.Sequence, event.CreationDate) + return p.view.DeleteProjectMember(event.AggregateID, member.UserID, event) case proj_es_model.ProjectRemoved: return p.view.DeleteProjectMembersByProjectID(event.AggregateID) default: - return p.view.ProcessedProjectMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectMemberSequence(event) } if err != nil { return err } - return p.view.PutProjectMember(member, member.Sequence, event.CreationDate) + return p.view.PutProjectMember(member, event) } func (p *ProjectMember) processUser(event *models.Event) (err error) { @@ -96,7 +134,7 @@ func (p *ProjectMember) processUser(event *models.Event) (err error) { return err } if len(members) == 0 { - return p.view.ProcessedProjectMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectMemberSequence(event) } user, err := p.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -105,9 +143,9 @@ func (p *ProjectMember) processUser(event *models.Event) (err error) { for _, member := range members { p.fillUserData(member, user) } - return p.view.PutProjectMembers(members, event.Sequence, event.CreationDate) + return p.view.PutProjectMembers(members, event) default: - return p.view.ProcessedProjectMemberSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectMemberSequence(event) } return nil } diff --git a/internal/management/repository/eventsourcing/handler/project_role.go b/internal/management/repository/eventsourcing/handler/project_role.go index 0dba4b14e8..3d89ba4a9f 100644 --- a/internal/management/repository/eventsourcing/handler/project_role.go +++ b/internal/management/repository/eventsourcing/handler/project_role.go @@ -3,7 +3,9 @@ package handler import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -11,21 +13,57 @@ import ( view_model "github.com/caos/zitadel/internal/project/repository/view/model" ) -type ProjectRole struct { - handler - projectEvents *proj_event.ProjectEventstore -} - const ( projectRoleTable = "management.project_roles" ) +type ProjectRole struct { + handler + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +func newProjectRole( + handler handler, + projectEvents *proj_event.ProjectEventstore, +) *ProjectRole { + h := &ProjectRole{ + handler: handler, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (m *ProjectRole) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (p *ProjectRole) ViewModel() string { return projectRoleTable } +func (_ *ProjectRole) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.ProjectAggregate} +} + +func (p *ProjectRole) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := p.view.GetLatestProjectRoleSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (p *ProjectRole) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestProjectRoleSequence() + sequence, err := p.view.GetLatestProjectRoleSequence("") if err != nil { return nil, err } @@ -52,16 +90,16 @@ func (p *ProjectRole) Reduce(event *models.Event) (err error) { if err != nil { return err } - return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event.Sequence, event.CreationDate) + return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event) case es_model.ProjectRemoved: return p.view.DeleteProjectRolesByProjectID(event.AggregateID) default: - return p.view.ProcessedProjectRoleSequence(event.Sequence, event.CreationDate) + return p.view.ProcessedProjectRoleSequence(event) } if err != nil { return err } - return p.view.PutProjectRole(role, event.CreationDate) + return p.view.PutProjectRole(role, event) } func (p *ProjectRole) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/handler/user.go b/internal/management/repository/eventsourcing/handler/user.go index 0721e0fcd3..bf415d1602 100644 --- a/internal/management/repository/eventsourcing/handler/user.go +++ b/internal/management/repository/eventsourcing/handler/user.go @@ -2,14 +2,14 @@ package handler import ( "context" - iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/logging" - "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" @@ -17,29 +17,68 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type User struct { - handler - eventstore eventstore.Eventstore - orgEvents *org_events.OrgEventstore - iamEvents *iam_es.IAMEventstore - iamID string -} - const ( userTable = "management.users" ) +type User struct { + handler + orgEvents *org_events.OrgEventstore + iamEvents *iam_es.IAMEventstore + iamID string + subscription *eventstore.Subscription +} + +func newUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + iamID string, +) *User { + h := &User{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (m *User) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (u *User) ViewModel() string { return userTable } +func (_ *User) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (u *User) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (u *User) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserSequence() + sequence, err := u.view.GetLatestUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } @@ -115,14 +154,14 @@ func (u *User) ProcessUser(event *models.Event) (err error) { } err = u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUser(event.AggregateID, event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } if err != nil { return err } - return u.view.PutUser(user, user.Sequence, event.CreationDate) + return u.view.PutUser(user, event) } func (u *User) ProcessOrg(event *models.Event) (err error) { @@ -136,7 +175,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserSequence(event) } } @@ -159,7 +198,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.SetLoginNames(policy, org.Domains) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { @@ -175,7 +214,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { } } if !policy.UserLoginMustBeDomain { - return nil + return u.view.ProcessedUserSequence(event) } users, err := u.view.UsersByOrgID(event.AggregateID) if err != nil { @@ -184,7 +223,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) } - return u.view.PutUsers(users, event.Sequence, event.CreationDate) + return u.view.PutUsers(users, event) } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/user_external_idps.go b/internal/management/repository/eventsourcing/handler/user_external_idps.go index de57a8f0f2..059a15c9bd 100644 --- a/internal/management/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/management/repository/eventsourcing/handler/user_external_idps.go @@ -2,21 +2,27 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" + iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/eventsourcing" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model" +) - "github.com/caos/zitadel/internal/eventstore/models" - es_models "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" - iam_model "github.com/caos/zitadel/internal/iam/model" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" +const ( + externalIDPTable = "management.user_external_idps" ) type ExternalIDP struct { @@ -24,27 +30,63 @@ type ExternalIDP struct { systemDefaults systemdefaults.SystemDefaults iamEvents *eventsourcing.IAMEventstore orgEvents *org_es.OrgEventstore + subscription *eventstore.Subscription } -const ( - externalIDPTable = "management.user_external_idps" -) +func newExternalIDP( + handler handler, + systemDefaults systemdefaults.SystemDefaults, + iamEvents *eventsourcing.IAMEventstore, + orgEvents *org_es.OrgEventstore, +) *ExternalIDP { + h := &ExternalIDP{ + handler: handler, + systemDefaults: systemDefaults, + iamEvents: iamEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *ExternalIDP) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (i *ExternalIDP) ViewModel() string { return externalIDPTable } -func (i *ExternalIDP) EventQuery() (*models.SearchQuery, error) { - sequence, err := i.view.GetLatestExternalIDPSequence() +func (_ *ExternalIDP) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate} +} + +func (i *ExternalIDP) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := i.view.GetLatestExternalIDPSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (i *ExternalIDP) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := i.view.GetLatestExternalIDPSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(model.UserAggregate, iam_es_model.IAMAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(i.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (i *ExternalIDP) Reduce(event *models.Event) (err error) { +func (i *ExternalIDP) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case model.UserAggregate: err = i.processUser(event) @@ -54,7 +96,7 @@ func (i *ExternalIDP) Reduce(event *models.Event) (err error) { return err } -func (i *ExternalIDP) processUser(event *models.Event) (err error) { +func (i *ExternalIDP) processUser(event *es_models.Event) (err error) { externalIDP := new(usr_view_model.ExternalIDPView) switch event.Type { case model.HumanExternalIDPAdded: @@ -68,19 +110,19 @@ func (i *ExternalIDP) processUser(event *models.Event) (err error) { if err != nil { return err } - return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDP(externalIDP.ExternalUserID, externalIDP.IDPConfigID, event) case model.UserRemoved: - return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return i.view.DeleteExternalIDPsByUserID(event.AggregateID, event) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } if err != nil { return err } - return i.view.PutExternalIDP(externalIDP, externalIDP.Sequence, event.CreationDate) + return i.view.PutExternalIDP(externalIDP, event) } -func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { +func (i *ExternalIDP) processIdpConfig(event *es_models.Event) (err error) { switch event.Type { case iam_es_model.IDPConfigChanged, org_es_model.IDPConfigChanged: configView := new(iam_view_model.IDPConfigView) @@ -105,9 +147,9 @@ func (i *ExternalIDP) processIdpConfig(event *models.Event) (err error) { for _, provider := range exterinalIDPs { i.fillConfigData(provider, config) } - return i.view.PutExternalIDPs(event.Sequence, event.CreationDate, exterinalIDPs...) + return i.view.PutExternalIDPs(event, exterinalIDPs...) default: - return i.view.ProcessedExternalIDPSequence(event.Sequence, event.CreationDate) + return i.view.ProcessedExternalIDPSequence(event) } return nil } @@ -128,7 +170,7 @@ func (i *ExternalIDP) fillConfigData(externalIDP *usr_view_model.ExternalIDPView externalIDP.IDPName = config.Name } -func (i *ExternalIDP) OnError(event *models.Event, err error) error { +func (i *ExternalIDP) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-4Rsu8", "id", event.AggregateID).WithError(err).Warn("something went wrong in idp provider handler") return spooler.HandleError(event, err, i.view.GetLatestExternalIDPFailedEvent, i.view.ProcessedExternalIDPFailedEvent, i.view.ProcessedExternalIDPSequence, i.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/user_grant.go b/internal/management/repository/eventsourcing/handler/user_grant.go index 0ffe6955ac..a4f0869783 100644 --- a/internal/management/repository/eventsourcing/handler/user_grant.go +++ b/internal/management/repository/eventsourcing/handler/user_grant.go @@ -3,7 +3,12 @@ package handler import ( "context" + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" proj_model "github.com/caos/zitadel/internal/project/model" @@ -13,42 +18,75 @@ import ( usr_events "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" grant_es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model" ) -type UserGrant struct { - handler - eventstore eventstore.Eventstore - projectEvents *proj_event.ProjectEventstore - userEvents *usr_events.UserEventstore - orgEvents *org_events.OrgEventstore -} - const ( userGrantTable = "management.user_grants" ) +type UserGrant struct { + handler + projectEvents *proj_event.ProjectEventstore + userEvents *usr_events.UserEventstore + orgEvents *org_events.OrgEventstore + subscription *eventstore.Subscription +} + +func newUserGrant( + handler handler, + projectEvents *proj_event.ProjectEventstore, + userEvents *usr_events.UserEventstore, + orgEvents *org_events.OrgEventstore, +) *UserGrant { + h := &UserGrant{ + handler: handler, + projectEvents: projectEvents, + userEvents: userEvents, + orgEvents: orgEvents, + } + + h.subscribe() + + return h +} + +func (m *UserGrant) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} + func (u *UserGrant) ViewModel() string { return userGrantTable } -func (u *UserGrant) EventQuery() (*models.SearchQuery, error) { - sequence, err := u.view.GetLatestUserGrantSequence() +func (_ *UserGrant) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{grant_es_model.UserGrantAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate} +} + +func (u *UserGrant) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserGrantSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (u *UserGrant) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := u.view.GetLatestUserGrantSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(grant_es_model.UserGrantAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate). + AggregateTypeFilter(u.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (u *UserGrant) Reduce(event *models.Event) (err error) { +func (u *UserGrant) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case grant_es_model.UserGrantAggregate: err = u.processUserGrant(event) @@ -60,7 +98,7 @@ func (u *UserGrant) Reduce(event *models.Event) (err error) { return err } -func (u *UserGrant) processUserGrant(event *models.Event) (err error) { +func (u *UserGrant) processUserGrant(event *es_models.Event) (err error) { grant := new(view_model.UserGrantView) switch event.Type { case grant_es_model.UserGrantAdded: @@ -79,17 +117,17 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) { } err = grant.AppendEvent(event) case grant_es_model.UserGrantRemoved, grant_es_model.UserGrantCascadeRemoved: - return u.view.DeleteUserGrant(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteUserGrant(event.AggregateID, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } if err != nil { return err } - return u.view.PutUserGrant(grant, grant.Sequence, event.CreationDate) + return u.view.PutUserGrant(grant, event) } -func (u *UserGrant) processUser(event *models.Event) (err error) { +func (u *UserGrant) processUser(event *es_models.Event) (err error) { switch event.Type { case usr_es_model.UserProfileChanged, usr_es_model.UserEmailChanged, @@ -101,7 +139,7 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { return err } if len(grants) == 0 { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } user, err := u.userEvents.UserByID(context.Background(), event.AggregateID) if err != nil { @@ -110,14 +148,13 @@ func (u *UserGrant) processUser(event *models.Event) (err error) { for _, grant := range grants { u.fillUserData(grant, user) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } - return nil } -func (u *UserGrant) processProject(event *models.Event) (err error) { +func (u *UserGrant) processProject(event *es_models.Event) (err error) { switch event.Type { case proj_es_model.ProjectChanged: grants, err := u.view.UserGrantsByProjectID(event.AggregateID) @@ -125,7 +162,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { return err } if len(grants) == 0 { - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } project, err := u.projectEvents.ProjectByID(context.Background(), event.AggregateID) if err != nil { @@ -134,11 +171,10 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { for _, grant := range grants { u.fillProjectData(grant, project) } - return u.view.PutUserGrants(grants, event.Sequence, event.CreationDate) + return u.view.PutUserGrants(grants, event) default: - return u.view.ProcessedUserGrantSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedUserGrantSequence(event) } - return nil } func (u *UserGrant) fillData(grant *view_model.UserGrantView, resourceOwner string) (err error) { @@ -189,7 +225,7 @@ func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model. } } -func (u *UserGrant) OnError(event *models.Event, err error) error { +func (u *UserGrant) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-8is4s", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler") return spooler.HandleError(event, err, u.view.GetLatestUserGrantFailedEvent, u.view.ProcessedUserGrantFailedEvent, u.view.ProcessedUserGrantSequence, u.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/handler/user_membership.go b/internal/management/repository/eventsourcing/handler/user_membership.go index 8537905d47..9358acc4e2 100644 --- a/internal/management/repository/eventsourcing/handler/user_membership.go +++ b/internal/management/repository/eventsourcing/handler/user_membership.go @@ -2,51 +2,90 @@ package handler import ( "context" + + "github.com/caos/logging" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/models" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" + "github.com/caos/zitadel/internal/eventstore/spooler" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" - "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/eventstore/models" - es_models "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/spooler" - org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/model" + "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/view/model" ) +const ( + userMembershipTable = "management.user_memberships" +) + type UserMembership struct { handler orgEvents *org_event.OrgEventstore projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription } -const ( - userMembershipTable = "management.user_memberships" -) +func newUserMembership( + handler handler, + orgEvents *org_event.OrgEventstore, + projectEvents *proj_event.ProjectEventstore, +) *UserMembership { + h := &UserMembership{ + handler: handler, + orgEvents: orgEvents, + projectEvents: projectEvents, + } + + h.subscribe() + + return h +} + +func (m *UserMembership) subscribe() { + m.subscription = m.es.Subscribe(m.AggregateTypes()...) + go func() { + for event := range m.subscription.Events { + query.ReduceEvent(m, event) + } + }() +} func (m *UserMembership) ViewModel() string { return userMembershipTable } -func (m *UserMembership) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestUserMembershipSequence() +func (_ *UserMembership) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate} +} + +func (u *UserMembership) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := u.view.GetLatestUserMembershipSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *UserMembership) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := m.view.GetLatestUserMembershipSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate, model.UserAggregate). + AggregateTypeFilter(m.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (m *UserMembership) Reduce(event *models.Event) (err error) { +func (m *UserMembership) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case iam_es_model.IAMAggregate: - err = m.processIam(event) + err = m.processIAM(event) case org_es_model.OrgAggregate: err = m.processOrg(event) case proj_es_model.ProjectAggregate: @@ -57,7 +96,7 @@ func (m *UserMembership) Reduce(event *models.Event) (err error) { return err } -func (m *UserMembership) processIam(event *models.Event) (err error) { +func (m *UserMembership) processIAM(event *es_models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { @@ -73,21 +112,21 @@ func (m *UserMembership) processIam(event *models.Event) (err error) { } err = member.AppendEvent(event) case iam_es_model.IAMMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillIamDisplayName(member *usr_es_model.UserMembershipView) { member.DisplayName = member.AggregateID } -func (m *UserMembership) processOrg(event *models.Event) (err error) { +func (m *UserMembership) processOrg(event *es_models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { @@ -103,16 +142,16 @@ func (m *UserMembership) processOrg(event *models.Event) (err error) { } err = member.AppendEvent(event) case org_es_model.OrgMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event) case org_es_model.OrgChanged: return m.updateOrgDisplayName(event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillOrgDisplayName(member *usr_es_model.UserMembershipView) (err error) { @@ -124,7 +163,7 @@ func (m *UserMembership) fillOrgDisplayName(member *usr_es_model.UserMembershipV return nil } -func (m *UserMembership) updateOrgDisplayName(event *models.Event) error { +func (m *UserMembership) updateOrgDisplayName(event *es_models.Event) error { org, err := m.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.AggregateID)) if err != nil { return err @@ -137,10 +176,10 @@ func (m *UserMembership) updateOrgDisplayName(event *models.Event) error { for _, membership := range memberships { membership.DisplayName = org.Name } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } -func (m *UserMembership) processProject(event *models.Event) (err error) { +func (m *UserMembership) processProject(event *es_models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) if err != nil { @@ -156,7 +195,7 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event) case proj_es_model.ProjectGrantMemberChanged: member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant) if err != nil { @@ -164,20 +203,20 @@ func (m *UserMembership) processProject(event *models.Event) (err error) { } err = member.AppendEvent(event) case proj_es_model.ProjectGrantMemberRemoved: - return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event) case proj_es_model.ProjectChanged: return m.updateProjectDisplayName(event) case proj_es_model.ProjectRemoved: - return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event) case proj_es_model.ProjectGrantRemoved: - return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } if err != nil { return err } - return m.view.PutUserMembership(member, event.Sequence, event.CreationDate) + return m.view.PutUserMembership(member, event) } func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembershipView) (err error) { @@ -189,7 +228,7 @@ func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembers return nil } -func (m *UserMembership) updateProjectDisplayName(event *models.Event) error { +func (m *UserMembership) updateProjectDisplayName(event *es_models.Event) error { project, err := m.projectEvents.ProjectByID(context.Background(), event.AggregateID) if err != nil { return err @@ -202,19 +241,19 @@ func (m *UserMembership) updateProjectDisplayName(event *models.Event) error { for _, membership := range memberships { membership.DisplayName = project.Name } - return m.view.BulkPutUserMemberships(memberships, event.Sequence, event.CreationDate) + return m.view.BulkPutUserMemberships(memberships, event) } -func (m *UserMembership) processUser(event *models.Event) (err error) { +func (m *UserMembership) processUser(event *es_models.Event) (err error) { switch event.Type { case model.UserRemoved: - return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event.Sequence, event.CreationDate) + return m.view.DeleteUserMembershipsByUserID(event.AggregateID, event) default: - return m.view.ProcessedUserMembershipSequence(event.Sequence, event.CreationDate) + return m.view.ProcessedUserMembershipSequence(event) } } -func (m *UserMembership) OnError(event *models.Event, err error) error { +func (m *UserMembership) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-Fwer2", "id", event.AggregateID).WithError(err).Warn("something went wrong in user membership handler") return spooler.HandleError(event, err, m.view.GetLatestUserMembershipFailedEvent, m.view.ProcessedUserMembershipFailedEvent, m.view.ProcessedUserMembershipSequence, m.errorCountUntilSkip) } diff --git a/internal/management/repository/eventsourcing/view/application.go b/internal/management/repository/eventsourcing/view/application.go index 46672fc74a..c9b4a6fbd0 100644 --- a/internal/management/repository/eventsourcing/view/application.go +++ b/internal/management/repository/eventsourcing/view/application.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,40 +25,40 @@ func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) return view.SearchApplications(v.Db, applicationTable, request) } -func (v *View) PutApplication(app *model.ApplicationView, eventTimestamp time.Time) error { +func (v *View) PutApplication(app *model.ApplicationView, event *models.Event) error { err := view.PutApplication(v.Db, applicationTable, app) - if err != nil { + if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedApplicationSequence(app.Sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) PutApplications(apps []*model.ApplicationView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Event) error { err := view.PutApplications(v.Db, applicationTable, apps...) if err != nil { return err } - return v.ProcessedApplicationSequence(sequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } -func (v *View) DeleteApplication(appID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteApplication(appID string, event *models.Event) error { err := view.DeleteApplication(v.Db, applicationTable, appID) if err != nil { - return nil + return err } - return v.ProcessedApplicationSequence(eventSequence, eventTimestamp) + return v.ProcessedApplicationSequence(event) } func (v *View) DeleteApplicationsByProjectID(projectID string) error { return view.DeleteApplicationsByProjectID(v.Db, applicationTable, projectID) } -func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(applicationTable) +func (v *View) GetLatestApplicationSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(applicationTable, aggregateType) } -func (v *View) ProcessedApplicationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(applicationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedApplicationSequence(event *models.Event) error { + return v.saveCurrentSequence(applicationTable, event) } func (v *View) UpdateApplicationSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/external_idps.go b/internal/management/repository/eventsourcing/view/external_idps.go index c2944c7a5f..0b8eb3648e 100644 --- a/internal/management/repository/eventsourcing/view/external_idps.go +++ b/internal/management/repository/eventsourcing/view/external_idps.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -33,43 +33,43 @@ func (v *View) SearchExternalIDPs(request *usr_model.ExternalIDPSearchRequest) ( return view.SearchExternalIDPs(v.Db, externalIDPTable, request) } -func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutExternalIDP(externalIDP *model.ExternalIDPView, event *models.Event) error { err := view.PutExternalIDP(v.Db, externalIDPTable, externalIDP) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) PutExternalIDPs(sequence uint64, eventTimestamp time.Time, externalIDPs ...*model.ExternalIDPView) error { +func (v *View) PutExternalIDPs(event *models.Event, externalIDPs ...*model.ExternalIDPView) error { err := view.PutExternalIDPs(v.Db, externalIDPTable, externalIDPs...) if err != nil { return err } - return v.ProcessedExternalIDPSequence(sequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDP(externalUserID, idpConfigID string, event *models.Event) error { err := view.DeleteExternalIDP(v.Db, externalIDPTable, externalUserID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) DeleteExternalIDPsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteExternalIDPsByUserID(userID string, event *models.Event) error { err := view.DeleteExternalIDPsByUserID(v.Db, externalIDPTable, userID) if err != nil { return err } - return v.ProcessedExternalIDPSequence(eventSequence, eventTimestamp) + return v.ProcessedExternalIDPSequence(event) } -func (v *View) GetLatestExternalIDPSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(externalIDPTable) +func (v *View) GetLatestExternalIDPSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(externalIDPTable, aggregateType) } -func (v *View) ProcessedExternalIDPSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(externalIDPTable, eventSequence, eventTimestamp) +func (v *View) ProcessedExternalIDPSequence(event *models.Event) error { + return v.saveCurrentSequence(externalIDPTable, event) } func (v *View) UpdateExternalIDPSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/idp_configs.go b/internal/management/repository/eventsourcing/view/idp_configs.go index a9dfde45da..5770f36b38 100644 --- a/internal/management/repository/eventsourcing/view/idp_configs.go +++ b/internal/management/repository/eventsourcing/view/idp_configs.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -21,28 +21,28 @@ func (v *View) SearchIDPConfigs(request *iam_model.IDPConfigSearchRequest) ([]*i return view.SearchIDPs(v.Db, idpConfigTable, request) } -func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPConfig(idp *iam_es_model.IDPConfigView, event *models.Event) error { err := view.PutIDP(v.Db, idpConfigTable, idp) if err != nil { return err } - return v.ProcessedIDPConfigSequence(sequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) DeleteIDPConfig(idpID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPConfig(idpID string, event *models.Event) error { err := view.DeleteIDP(v.Db, idpConfigTable, idpID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPConfigSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPConfigSequence(event) } -func (v *View) GetLatestIDPConfigSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpConfigTable) +func (v *View) GetLatestIDPConfigSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpConfigTable, aggregateType) } -func (v *View) ProcessedIDPConfigSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpConfigTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPConfigSequence(event *models.Event) error { + return v.saveCurrentSequence(idpConfigTable, event) } func (v *View) UpdateIDPConfigSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/idp_providers.go b/internal/management/repository/eventsourcing/view/idp_providers.go index 207feb621f..a293ec1637 100644 --- a/internal/management/repository/eventsourcing/view/idp_providers.go +++ b/internal/management/repository/eventsourcing/view/idp_providers.go @@ -2,11 +2,11 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -25,44 +25,44 @@ func (v *View) SearchIDPProviders(request *iam_model.IDPProviderSearchRequest) ( return view.SearchIDPProviders(v.Db, idpProviderTable, request) } -func (v *View) PutIDPProvider(provider *model.IDPProviderView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutIDPProvider(provider *model.IDPProviderView, event *models.Event) error { err := view.PutIDPProvider(v.Db, idpProviderTable, provider) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) PutIDPProviders(sequence uint64, eventTimestamp time.Time, providers ...*model.IDPProviderView) error { +func (v *View) PutIDPProviders(event *models.Event, providers ...*model.IDPProviderView) error { err := view.PutIDPProviders(v.Db, idpProviderTable, providers...) if err != nil { return err } - return v.ProcessedIDPProviderSequence(sequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvider(aggregateID, idpConfigID string, event *models.Event) error { err := view.DeleteIDPProvider(v.Db, idpProviderTable, aggregateID, idpConfigID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteIDPProvidersByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteIDPProvidersByAggregateID(v.Db, idpProviderTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedIDPProviderSequence(eventSequence, eventTimestamp) + return v.ProcessedIDPProviderSequence(event) } -func (v *View) GetLatestIDPProviderSequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(idpProviderTable) +func (v *View) GetLatestIDPProviderSequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(idpProviderTable, aggregateType) } -func (v *View) ProcessedIDPProviderSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(idpProviderTable, eventSequence, eventTimestamp) +func (v *View) ProcessedIDPProviderSequence(event *models.Event) error { + return v.saveCurrentSequence(idpProviderTable, event) } func (v *View) UpdateIDPProviderSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/label_policies.go b/internal/management/repository/eventsourcing/view/label_policies.go index 27b234fc29..05bb9550a4 100644 --- a/internal/management/repository/eventsourcing/view/label_policies.go +++ b/internal/management/repository/eventsourcing/view/label_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LabelPolicyByAggregateID(aggregateID string) (*model.LabelPolicyV return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTable, aggregateID) } -func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error { err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy) if err != nil { return err } - return v.ProcessedLabelPolicySequence(sequence, eventTimestamp) + return v.ProcessedLabelPolicySequence(event) } -func (v *View) DeleteLabelPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLabelPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLabelPolicy(v.Db, labelPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLabelPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLabelPolicySequence(event) } -func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(labelPolicyTable) +func (v *View) GetLatestLabelPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(labelPolicyTable, aggregateType) } -func (v *View) ProcessedLabelPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(labelPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLabelPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(labelPolicyTable, event) } func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/login_policies.go b/internal/management/repository/eventsourcing/view/login_policies.go index c37f4283d6..56e6e80f8f 100644 --- a/internal/management/repository/eventsourcing/view/login_policies.go +++ b/internal/management/repository/eventsourcing/view/login_policies.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) LoginPolicyByAggregateID(aggregateID string) (*model.LoginPolicyV return view.GetLoginPolicyByAggregateID(v.Db, loginPolicyTable, aggregateID) } -func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutLoginPolicy(policy *model.LoginPolicyView, event *models.Event) error { err := view.PutLoginPolicy(v.Db, loginPolicyTable, policy) if err != nil { return err } - return v.ProcessedLoginPolicySequence(sequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) DeleteLoginPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteLoginPolicy(aggregateID string, event *models.Event) error { err := view.DeleteLoginPolicy(v.Db, loginPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedLoginPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedLoginPolicySequence(event) } -func (v *View) GetLatestLoginPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(loginPolicyTable) +func (v *View) GetLatestLoginPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(loginPolicyTable, aggregateType) } -func (v *View) ProcessedLoginPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(loginPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedLoginPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(loginPolicyTable, event) } func (v *View) UpdateLoginPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/machine_keys.go b/internal/management/repository/eventsourcing/view/machine_keys.go index b037137c82..c861cfd58c 100644 --- a/internal/management/repository/eventsourcing/view/machine_keys.go +++ b/internal/management/repository/eventsourcing/view/machine_keys.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,39 +25,36 @@ func (v *View) SearchMachineKeys(request *usr_model.MachineKeySearchRequest) ([] return view.SearchMachineKeys(v.Db, machineKeyTable, request) } -func (v *View) PutMachineKey(org *model.MachineKeyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutMachineKey(org *model.MachineKeyView, event *models.Event) error { err := view.PutMachineKey(v.Db, machineKeyTable, org) if err != nil { return err } - if sequence != 0 { - return v.ProcessedMachineKeySequence(sequence, eventTimestamp) - } - return nil + return v.ProcessedMachineKeySequence(event) } -func (v *View) DeleteMachineKey(keyID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) DeleteMachineKeysByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedMachineKeySequence(eventSequence, eventTimestamp) + return v.ProcessedMachineKeySequence(event) } -func (v *View) GetLatestMachineKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(machineKeyTable) +func (v *View) GetLatestMachineKeySequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(machineKeyTable, aggregateType) } -func (v *View) ProcessedMachineKeySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(machineKeyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedMachineKeySequence(event *models.Event) error { + return v.saveCurrentSequence(machineKeyTable, event) } func (v *View) UpdateMachineKeySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/org.go b/internal/management/repository/eventsourcing/view/org.go index a5de5fdb46..3a53b92031 100644 --- a/internal/management/repository/eventsourcing/view/org.go +++ b/internal/management/repository/eventsourcing/view/org.go @@ -1,10 +1,10 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" org_view "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -15,12 +15,12 @@ func (v *View) OrgByID(orgID string) (*model.OrgView, error) { return org_view.OrgByID(v.Db, orgTable, orgID) } -func (v *View) PutOrg(org *model.OrgView, eventTimestamp time.Time) error { +func (v *View) PutOrg(org *model.OrgView, event *models.Event) error { err := org_view.PutOrg(v.Db, orgTable, org) if err != nil { return err } - return v.ProcessedOrgSequence(org.Sequence, eventTimestamp) + return v.ProcessedOrgSequence(event) } func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) { @@ -35,10 +35,10 @@ func (v *View) UpdateOrgSpoolerRunTimestamp() error { return v.updateSpoolerRunSequence(orgTable) } -func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgTable) +func (v *View) GetLatestOrgSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable, aggregateType) } -func (v *View) ProcessedOrgSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgSequence(event *models.Event) error { + return v.saveCurrentSequence(orgTable, event) } diff --git a/internal/management/repository/eventsourcing/view/org_domain.go b/internal/management/repository/eventsourcing/view/org_domain.go index 94f3f84c95..45ee4ad912 100644 --- a/internal/management/repository/eventsourcing/view/org_domain.go +++ b/internal/management/repository/eventsourcing/view/org_domain.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,39 +29,36 @@ func (v *View) SearchOrgDomains(request *org_model.OrgDomainSearchRequest) ([]*m return view.SearchOrgDomains(v.Db, orgDomainTable, request) } -func (v *View) PutOrgDomain(org *model.OrgDomainView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgDomain(org *model.OrgDomainView, event *models.Event) error { err := view.PutOrgDomain(v.Db, orgDomainTable, org) if err != nil { return err } - if sequence != 0 { - return v.ProcessedOrgDomainSequence(sequence, eventTimestamp) - } - return nil + return v.ProcessedOrgDomainSequence(event) } -func (v *View) PutOrgDomains(domains []*model.OrgDomainView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgDomains(domains []*model.OrgDomainView, event *models.Event) error { err := view.PutOrgDomains(v.Db, orgDomainTable, domains...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) DeleteOrgDomain(orgID, domain string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgDomain(orgID, domain string, event *models.Event) error { err := view.DeleteOrgDomain(v.Db, orgDomainTable, orgID, domain) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedOrgDomainSequence(eventSequence, eventTimestamp) + return v.ProcessedOrgDomainSequence(event) } -func (v *View) GetLatestOrgDomainSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgDomainTable) +func (v *View) GetLatestOrgDomainSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgDomainTable, aggregateType) } -func (v *View) ProcessedOrgDomainSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgDomainTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgDomainSequence(event *models.Event) error { + return v.saveCurrentSequence(orgDomainTable, event) } func (v *View) UpdateOrgDomainSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/org_iam_policy.go b/internal/management/repository/eventsourcing/view/org_iam_policy.go index db9d113155..90d3de2b9c 100644 --- a/internal/management/repository/eventsourcing/view/org_iam_policy.go +++ b/internal/management/repository/eventsourcing/view/org_iam_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) OrgIAMPolicyByAggregateID(aggregateID string) (*model.OrgIAMPolic return view.GetOrgIAMPolicyByAggregateID(v.Db, orgIAMPolicyTable, aggregateID) } -func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgIAMPolicy(policy *model.OrgIAMPolicyView, event *models.Event) error { err := view.PutOrgIAMPolicy(v.Db, orgIAMPolicyTable, policy) if err != nil { return err } - return v.ProcessedOrgIAMPolicySequence(sequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) DeleteOrgIAMPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgIAMPolicy(aggregateID string, event *models.Event) error { err := view.DeleteOrgIAMPolicy(v.Db, orgIAMPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedOrgIAMPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedOrgIAMPolicySequence(event) } -func (v *View) GetLatestOrgIAMPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(orgIAMPolicyTable) +func (v *View) GetLatestOrgIAMPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(orgIAMPolicyTable, aggregateType) } -func (v *View) ProcessedOrgIAMPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgIAMPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgIAMPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(orgIAMPolicyTable, event) } func (v *View) UpdateOrgIAMPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/org_member.go b/internal/management/repository/eventsourcing/view/org_member.go index d1cb20b3c0..ee6deac739 100644 --- a/internal/management/repository/eventsourcing/view/org_member.go +++ b/internal/management/repository/eventsourcing/view/org_member.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/org/repository/view" "github.com/caos/zitadel/internal/org/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,44 +25,44 @@ func (v *View) OrgMembersByUserID(userID string) ([]*model.OrgMemberView, error) return view.OrgMembersByUserID(v.Db, orgMemberTable, userID) } -func (v *View) PutOrgMember(member *model.OrgMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgMember(member *model.OrgMemberView, event *models.Event) error { err := view.PutOrgMember(v.Db, orgMemberTable, member) if err != nil { return err } - return v.ProcessedOrgMemberSequence(sequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) PutOrgMembers(members []*model.OrgMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutOrgMembers(members []*model.OrgMemberView, event *models.Event) error { err := view.PutOrgMembers(v.Db, orgMemberTable, members...) if err != nil { return err } - return v.ProcessedOrgMemberSequence(sequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) DeleteOrgMember(orgID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgMember(orgID, userID string, event *models.Event) error { err := view.DeleteOrgMember(v.Db, orgMemberTable, orgID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedOrgMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) DeleteOrgMembersByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteOrgMembersByUserID(userID string, event *models.Event) error { err := view.DeleteOrgMembersByUserID(v.Db, orgMemberTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedOrgMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedOrgMemberSequence(event) } -func (v *View) GetLatestOrgMemberSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(orgMemberTable) +func (v *View) GetLatestOrgMemberSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(orgMemberTable, aggregateType) } -func (v *View) ProcessedOrgMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(orgMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedOrgMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(orgMemberTable, event) } func (v *View) UpdateOrgMemberSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/password_age_policy.go b/internal/management/repository/eventsourcing/view/password_age_policy.go index e37d650072..e1bd56a596 100644 --- a/internal/management/repository/eventsourcing/view/password_age_policy.go +++ b/internal/management/repository/eventsourcing/view/password_age_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordAgePolicyByAggregateID(aggregateID string) (*model.Passwo return view.GetPasswordAgePolicyByAggregateID(v.Db, passwordAgePolicyTable, aggregateID) } -func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordAgePolicy(policy *model.PasswordAgePolicyView, event *models.Event) error { err := view.PutPasswordAgePolicy(v.Db, passwordAgePolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordAgePolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) DeletePasswordAgePolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordAgePolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordAgePolicy(v.Db, passwordAgePolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordAgePolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordAgePolicySequence(event) } -func (v *View) GetLatestPasswordAgePolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordAgePolicyTable) +func (v *View) GetLatestPasswordAgePolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordAgePolicyTable, aggregateType) } -func (v *View) ProcessedPasswordAgePolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordAgePolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordAgePolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordAgePolicyTable, event) } func (v *View) UpdatePasswordAgePolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/password_complexity_policy.go b/internal/management/repository/eventsourcing/view/password_complexity_policy.go index 70895d13a9..80d9e2d90d 100644 --- a/internal/management/repository/eventsourcing/view/password_complexity_policy.go +++ b/internal/management/repository/eventsourcing/view/password_complexity_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordComplexityPolicyByAggregateID(aggregateID string) (*model return view.GetPasswordComplexityPolicyByAggregateID(v.Db, passwordComplexityPolicyTable, aggregateID) } -func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordComplexityPolicy(policy *model.PasswordComplexityPolicyView, event *models.Event) error { err := view.PutPasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordComplexityPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) DeletePasswordComplexityPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordComplexityPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordComplexityPolicy(v.Db, passwordComplexityPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordComplexityPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordComplexityPolicySequence(event) } -func (v *View) GetLatestPasswordComplexityPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordComplexityPolicyTable) +func (v *View) GetLatestPasswordComplexityPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordComplexityPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordComplexityPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordComplexityPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordComplexityPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordComplexityPolicyTable, event) } func (v *View) UpdatePasswordComplexityPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/password_lockout_policy.go b/internal/management/repository/eventsourcing/view/password_lockout_policy.go index 11e3955720..549f704c75 100644 --- a/internal/management/repository/eventsourcing/view/password_lockout_policy.go +++ b/internal/management/repository/eventsourcing/view/password_lockout_policy.go @@ -2,10 +2,10 @@ package view import ( "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/iam/repository/view" "github.com/caos/zitadel/internal/iam/repository/view/model" global_view "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -16,28 +16,28 @@ func (v *View) PasswordLockoutPolicyByAggregateID(aggregateID string) (*model.Pa return view.GetPasswordLockoutPolicyByAggregateID(v.Db, passwordLockoutPolicyTable, aggregateID) } -func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutPasswordLockoutPolicy(policy *model.PasswordLockoutPolicyView, event *models.Event) error { err := view.PutPasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, policy) if err != nil { return err } - return v.ProcessedPasswordLockoutPolicySequence(sequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) DeletePasswordLockoutPolicy(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeletePasswordLockoutPolicy(aggregateID string, event *models.Event) error { err := view.DeletePasswordLockoutPolicy(v.Db, passwordLockoutPolicyTable, aggregateID) if err != nil && !errors.IsNotFound(err) { return err } - return v.ProcessedPasswordLockoutPolicySequence(eventSequence, eventTimestamp) + return v.ProcessedPasswordLockoutPolicySequence(event) } -func (v *View) GetLatestPasswordLockoutPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(passwordLockoutPolicyTable) +func (v *View) GetLatestPasswordLockoutPolicySequence(aggregateType string) (*global_view.CurrentSequence, error) { + return v.latestSequence(passwordLockoutPolicyTable, aggregateType) } -func (v *View) ProcessedPasswordLockoutPolicySequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(passwordLockoutPolicyTable, eventSequence, eventTimestamp) +func (v *View) ProcessedPasswordLockoutPolicySequence(event *models.Event) error { + return v.saveCurrentSequence(passwordLockoutPolicyTable, event) } func (v *View) UpdatePasswordLockoutPolicySpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project.go b/internal/management/repository/eventsourcing/view/project.go index fede8830ee..f5ac609ddc 100644 --- a/internal/management/repository/eventsourcing/view/project.go +++ b/internal/management/repository/eventsourcing/view/project.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -20,28 +21,28 @@ func (v *View) SearchProjects(request *proj_model.ProjectViewSearchRequest) ([]* return view.SearchProjects(v.Db, projectTable, request) } -func (v *View) PutProject(project *model.ProjectView, eventTimestamp time.Time) error { +func (v *View) PutProject(project *model.ProjectView, event *models.Event) error { err := view.PutProject(v.Db, projectTable, project) if err != nil { return err } - return v.ProcessedProjectSequence(project.Sequence, eventTimestamp) + return v.ProcessedProjectSequence(event) } -func (v *View) DeleteProject(projectID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProject(projectID string, event *models.Event) error { err := view.DeleteProject(v.Db, projectTable, projectID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedProjectSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectSequence(event) } -func (v *View) GetLatestProjectSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectTable) +func (v *View) GetLatestProjectSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectTable, aggregateType) } -func (v *View) ProcessedProjectSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectSequence(event *models.Event) error { + return v.saveCurrentSequence(projectTable, event) } func (v *View) UpdateProjectSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_grant.go b/internal/management/repository/eventsourcing/view/project_grant.go index 64f1f6272e..ab54927a30 100644 --- a/internal/management/repository/eventsourcing/view/project_grant.go +++ b/internal/management/repository/eventsourcing/view/project_grant.go @@ -1,11 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,40 +32,40 @@ func (v *View) SearchProjectGrants(request *proj_model.ProjectGrantViewSearchReq return view.SearchProjectGrants(v.Db, grantedProjectTable, request) } -func (v *View) PutProjectGrant(grant *model.ProjectGrantView, eventTimestamp time.Time) error { +func (v *View) PutProjectGrant(grant *model.ProjectGrantView, event *models.Event) error { err := view.PutProjectGrant(v.Db, grantedProjectTable, grant) if err != nil { return err } - return v.ProcessedProjectGrantSequence(grant.Sequence, eventTimestamp) + return v.ProcessedProjectGrantSequence(event) } -func (v *View) PutProjectGrants(grants []*model.ProjectGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectGrants(grants []*model.ProjectGrantView, event *models.Event) error { err := view.PutProjectGrants(v.Db, grantedProjectTable, grants...) if err != nil { return err } - return v.ProcessedProjectGrantSequence(sequence, eventTimestamp) + return v.ProcessedProjectGrantSequence(event) } -func (v *View) DeleteProjectGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectGrant(grantID string, event *models.Event) error { err := view.DeleteProjectGrant(v.Db, grantedProjectTable, grantID) if err != nil { return err } - return v.ProcessedProjectGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectGrantSequence(event) } func (v *View) DeleteProjectGrantsByProjectID(projectID string) error { return view.DeleteProjectGrantsByProjectID(v.Db, grantedProjectTable, projectID) } -func (v *View) GetLatestProjectGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(grantedProjectTable) +func (v *View) GetLatestProjectGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(grantedProjectTable, aggregateType) } -func (v *View) ProcessedProjectGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(grantedProjectTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(grantedProjectTable, event) } func (v *View) UpdateProjectGrantSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_grant_member.go b/internal/management/repository/eventsourcing/view/project_grant_member.go index b9cf4391ba..7634cefbf5 100644 --- a/internal/management/repository/eventsourcing/view/project_grant_member.go +++ b/internal/management/repository/eventsourcing/view/project_grant_member.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,40 +29,40 @@ func (v *View) ProjectGrantMembersByUserID(userID string) ([]*model.ProjectGrant return view.ProjectGrantMembersByUserID(v.Db, projectGrantMemberTable, userID) } -func (v *View) PutProjectGrantMember(member *model.ProjectGrantMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectGrantMember(member *model.ProjectGrantMemberView, event *models.Event) error { err := view.PutProjectGrantMember(v.Db, projectGrantMemberTable, member) if err != nil { return err } - return v.ProcessedProjectGrantMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectGrantMemberSequence(event) } -func (v *View) PutProjectGrantMembers(members []*model.ProjectGrantMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectGrantMembers(members []*model.ProjectGrantMemberView, event *models.Event) error { err := view.PutProjectGrantMembers(v.Db, projectGrantMemberTable, members...) if err != nil { return err } - return v.ProcessedProjectGrantMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectGrantMemberSequence(event) } -func (v *View) DeleteProjectGrantMember(grantID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectGrantMember(grantID, userID string, event *models.Event) error { err := view.DeleteProjectGrantMember(v.Db, projectGrantMemberTable, grantID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedProjectGrantMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectGrantMemberSequence(event) } func (v *View) DeleteProjectGrantMembersByProjectID(projectID string) error { return view.DeleteProjectGrantMembersByProjectID(v.Db, projectGrantMemberTable, projectID) } -func (v *View) GetLatestProjectGrantMemberSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectGrantMemberTable) +func (v *View) GetLatestProjectGrantMemberSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectGrantMemberTable, aggregateType) } -func (v *View) ProcessedProjectGrantMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectGrantMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectGrantMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(projectGrantMemberTable, event) } func (v *View) UpdateProjectGrantMemberSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_member.go b/internal/management/repository/eventsourcing/view/project_member.go index c21dee73e2..09f3b31518 100644 --- a/internal/management/repository/eventsourcing/view/project_member.go +++ b/internal/management/repository/eventsourcing/view/project_member.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -28,40 +29,40 @@ func (v *View) ProjectMembersByUserID(userID string) ([]*model.ProjectMemberView return view.ProjectMembersByUserID(v.Db, projectMemberTable, userID) } -func (v *View) PutProjectMember(project *model.ProjectMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectMember(project *model.ProjectMemberView, event *models.Event) error { err := view.PutProjectMember(v.Db, projectMemberTable, project) if err != nil { return err } - return v.ProcessedProjectMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectMemberSequence(event) } -func (v *View) PutProjectMembers(project []*model.ProjectMemberView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutProjectMembers(project []*model.ProjectMemberView, event *models.Event) error { err := view.PutProjectMembers(v.Db, projectMemberTable, project...) if err != nil { return err } - return v.ProcessedProjectMemberSequence(sequence, eventTimestamp) + return v.ProcessedProjectMemberSequence(event) } -func (v *View) DeleteProjectMember(projectID, userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectMember(projectID, userID string, event *models.Event) error { err := view.DeleteProjectMember(v.Db, projectMemberTable, projectID, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedProjectMemberSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectMemberSequence(event) } func (v *View) DeleteProjectMembersByProjectID(projectID string) error { return view.DeleteProjectMembersByProjectID(v.Db, projectMemberTable, projectID) } -func (v *View) GetLatestProjectMemberSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectMemberTable) +func (v *View) GetLatestProjectMemberSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectMemberTable, aggregateType) } -func (v *View) ProcessedProjectMemberSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectMemberTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectMemberSequence(event *models.Event) error { + return v.saveCurrentSequence(projectMemberTable, event) } func (v *View) UpdateProjectMemberSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/project_role.go b/internal/management/repository/eventsourcing/view/project_role.go index 6703a93efd..5b251fa24b 100644 --- a/internal/management/repository/eventsourcing/view/project_role.go +++ b/internal/management/repository/eventsourcing/view/project_role.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/view" "github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -32,32 +33,32 @@ func (v *View) SearchProjectRoles(request *proj_model.ProjectRoleSearchRequest) return view.SearchProjectRoles(v.Db, projectRoleTable, request) } -func (v *View) PutProjectRole(project *model.ProjectRoleView, eventTimestamp time.Time) error { +func (v *View) PutProjectRole(project *model.ProjectRoleView, event *models.Event) error { err := view.PutProjectRole(v.Db, projectRoleTable, project) if err != nil { return err } - return v.ProcessedProjectRoleSequence(project.Sequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } -func (v *View) DeleteProjectRole(projectID, orgID, key string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteProjectRole(projectID, orgID, key string, event *models.Event) error { err := view.DeleteProjectRole(v.Db, projectRoleTable, projectID, orgID, key) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedProjectRoleSequence(eventSequence, eventTimestamp) + return v.ProcessedProjectRoleSequence(event) } func (v *View) DeleteProjectRolesByProjectID(projectID string) error { return view.DeleteProjectRolesByProjectID(v.Db, projectRoleTable, projectID) } -func (v *View) GetLatestProjectRoleSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(projectRoleTable) +func (v *View) GetLatestProjectRoleSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(projectRoleTable, aggregateType) } -func (v *View) ProcessedProjectRoleSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(projectRoleTable, eventSequence, eventTimestamp) +func (v *View) ProcessedProjectRoleSequence(event *models.Event) error { + return v.saveCurrentSequence(projectRoleTable, event) } func (v *View) UpdateProjectRoleSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/sequence.go b/internal/management/repository/eventsourcing/view/sequence.go index 82354c98e4..808ff58216 100644 --- a/internal/management/repository/eventsourcing/view/sequence.go +++ b/internal/management/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "management.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/management/repository/eventsourcing/view/user.go b/internal/management/repository/eventsourcing/view/user.go index 69fc14a270..d589b546f7 100644 --- a/internal/management/repository/eventsourcing/view/user.go +++ b/internal/management/repository/eventsourcing/view/user.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -40,39 +41,36 @@ func (v *View) UserMFAs(userID string) ([]*usr_model.MultiFactor, error) { return view.UserMFAs(v.Db, userTable, userID) } -func (v *View) PutUsers(user []*model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUsers(user []*model.UserView, event *models.Event) error { err := view.PutUsers(v.Db, userTable, user...) if err != nil { return err } - return v.ProcessedUserSequence(sequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) PutUser(user *model.UserView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUser(user *model.UserView, event *models.Event) error { err := view.PutUser(v.Db, userTable, user) if err != nil { return err } - if sequence != 0 { - return v.ProcessedUserSequence(sequence, eventTimestamp) - } - return nil + return v.ProcessedUserSequence(event) } -func (v *View) DeleteUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUser(userID string, event *models.Event) error { err := view.DeleteUser(v.Db, userTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserSequence(eventSequence, eventTimestamp) + return v.ProcessedUserSequence(event) } -func (v *View) GetLatestUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userTable) +func (v *View) GetLatestUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userTable, aggregateType) } -func (v *View) ProcessedUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserSequence(event *models.Event) error { + return v.saveCurrentSequence(userTable, event) } func (v *View) UpdateUserSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/user_grant.go b/internal/management/repository/eventsourcing/view/user_grant.go index 209fbfe6f4..15dd3220d2 100644 --- a/internal/management/repository/eventsourcing/view/user_grant.go +++ b/internal/management/repository/eventsourcing/view/user_grant.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" grant_model "github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/repository/view" "github.com/caos/zitadel/internal/usergrant/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -40,36 +41,36 @@ func (v *View) UserGrantsByOrgIDAndProjectID(orgID, projectID string) ([]*model. return view.UserGrantsByOrgIDAndProjectID(v.Db, userGrantTable, orgID, projectID) } -func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) error { err := view.PutUserGrant(v.Db, userGrantTable, grant) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) PutUserGrants(grants []*model.UserGrantView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserGrants(grants []*model.UserGrantView, event *models.Event) error { err := view.PutUserGrants(v.Db, userGrantTable, grants...) if err != nil { return err } - return v.ProcessedUserGrantSequence(sequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) DeleteUserGrant(grantID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserGrant(grantID string, event *models.Event) error { err := view.DeleteUserGrant(v.Db, userGrantTable, grantID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserGrantSequence(eventSequence, eventTimestamp) + return v.ProcessedUserGrantSequence(event) } -func (v *View) GetLatestUserGrantSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userGrantTable) +func (v *View) GetLatestUserGrantSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userGrantTable, aggregateType) } -func (v *View) ProcessedUserGrantSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userGrantTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserGrantSequence(event *models.Event) error { + return v.saveCurrentSequence(userGrantTable, event) } func (v *View) UpdateUserGrantSpoolerRunTimestamp() error { diff --git a/internal/management/repository/eventsourcing/view/user_membership.go b/internal/management/repository/eventsourcing/view/user_membership.go index 4c733dc875..90f64906a7 100644 --- a/internal/management/repository/eventsourcing/view/user_membership.go +++ b/internal/management/repository/eventsourcing/view/user_membership.go @@ -1,11 +1,12 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -24,60 +25,60 @@ func (v *View) SearchUserMemberships(request *usr_model.UserMembershipSearchRequ return view.SearchUserMemberships(v.Db, userMembershipTable, request) } -func (v *View) PutUserMembership(membership *model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutUserMembership(membership *model.UserMembershipView, event *models.Event) error { err := view.PutUserMembership(v.Db, userMembershipTable, membership) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, sequence uint64, eventTimestamp time.Time) error { +func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, event *models.Event) error { err := view.PutUserMemberships(v.Db, userMembershipTable, memberships...) if err != nil { return err } - return v.ProcessedUserMembershipSequence(sequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, event *models.Event) error { err := view.DeleteUserMembership(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByUserID(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByUserID(userID string, event *models.Event) error { err := view.DeleteUserMembershipsByUserID(v.Db, userMembershipTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, event *models.Event) error { err := view.DeleteUserMembershipsByAggregateIDAndObjectID(v.Db, userMembershipTable, aggregateID, objectID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedUserMembershipSequence(eventSequence, eventTimestamp) + return v.ProcessedUserMembershipSequence(event) } -func (v *View) GetLatestUserMembershipSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(userMembershipTable) +func (v *View) GetLatestUserMembershipSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(userMembershipTable, aggregateType) } -func (v *View) ProcessedUserMembershipSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(userMembershipTable, eventSequence, eventTimestamp) +func (v *View) ProcessedUserMembershipSequence(event *models.Event) error { + return v.saveCurrentSequence(userMembershipTable, event) } func (v *View) UpdateUserMembershipSpoolerRunTimestamp() error { diff --git a/internal/management/repository/user.go b/internal/management/repository/user.go index 92a2364c11..da8d81b359 100644 --- a/internal/management/repository/user.go +++ b/internal/management/repository/user.go @@ -32,6 +32,10 @@ type UserRepository interface { UserMFAs(ctx context.Context, userID string) ([]*model.MultiFactor, error) RemoveOTP(ctx context.Context, userID string) error + RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error + + GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error) + RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error) RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error diff --git a/internal/notification/repository/eventsourcing/handler/handler.go b/internal/notification/repository/eventsourcing/handler/handler.go index a6db3edb74..eb8035ed88 100644 --- a/internal/notification/repository/eventsourcing/handler/handler.go +++ b/internal/notification/repository/eventsourcing/handler/handler.go @@ -1,6 +1,9 @@ package handler import ( + "net/http" + "time" + "github.com/caos/logging" sd "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/types" @@ -12,8 +15,6 @@ import ( "github.com/caos/zitadel/internal/notification/repository/eventsourcing/view" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" - "net/http" - "time" ) type Configs map[string]*Config @@ -27,6 +28,12 @@ type handler struct { bulkLimit uint64 cycleDuration time.Duration errorCountUntilSkip uint64 + + es eventstore.Eventstore +} + +func (h *handler) Eventstore() eventstore.Eventstore { + return h.es } type EventstoreRepos struct { @@ -35,34 +42,33 @@ type EventstoreRepos struct { IAMEvents *iam_es.IAMEventstore } -func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler { +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []query.Handler { aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey) if err != nil { logging.Log("HANDL-s90ew").WithError(err).Debug("error create new aes crypto") } return []query.Handler{ - &NotifyUser{ - handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, - orgEvents: repos.OrgEvents, - iamEvents: repos.IAMEvents, - iamID: systemDefaults.IamID, - }, - &Notification{ - handler: handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount}, - eventstore: eventstore, - userEvents: repos.UserEvents, - systemDefaults: systemDefaults, - AesCrypto: aesCrypto, - i18n: i18n, - statikDir: dir, - }, + newNotifyUser( + handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, + repos.OrgEvents, + repos.IAMEvents, + systemDefaults.IamID, + ), + newNotification( + handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es}, + repos.UserEvents, + systemDefaults, + aesCrypto, + i18n, + dir, + ), } } func (configs Configs) cycleDuration(viewModel string) time.Duration { c, ok := configs[viewModel] if !ok { - return 1 * time.Second + return 3 * time.Minute } return c.MinimumCycleDuration.Duration } @@ -71,6 +77,10 @@ func (h *handler) MinimumCycleDuration() time.Duration { return h.cycleDuration } +func (h *handler) LockDuration() time.Duration { + return h.cycleDuration / 3 +} + func (h *handler) QueryLimit() uint64 { return h.bulkLimit } diff --git a/internal/notification/repository/eventsourcing/handler/notification.go b/internal/notification/repository/eventsourcing/handler/notification.go index 96ff4ae9ce..0d37914e3a 100644 --- a/internal/notification/repository/eventsourcing/handler/notification.go +++ b/internal/notification/repository/eventsourcing/handler/notification.go @@ -7,34 +7,22 @@ import ( "time" "github.com/caos/logging" - "github.com/caos/zitadel/internal/api/authz" sd "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/crypto" - "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/i18n" iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/notification/types" - "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" ) -type Notification struct { - handler - eventstore eventstore.Eventstore - userEvents *usr_event.UserEventstore - systemDefaults sd.SystemDefaults - AesCrypto crypto.EncryptionAlgorithm - i18n *i18n.Translator - statikDir http.FileSystem -} - const ( notificationTable = "notification.notifications" NotifyUserID = "NOTIFICATION" @@ -42,16 +30,69 @@ const ( labelPolicyTableDef = "adminapi.label_policies" ) +type Notification struct { + handler + userEvents *usr_event.UserEventstore + systemDefaults sd.SystemDefaults + AesCrypto crypto.EncryptionAlgorithm + i18n *i18n.Translator + statikDir http.FileSystem + subscription *eventstore.Subscription +} + +func newNotification( + handler handler, + userEvents *usr_event.UserEventstore, + defaults sd.SystemDefaults, + aesCrypto crypto.EncryptionAlgorithm, + translator *i18n.Translator, + statikDir http.FileSystem, +) *Notification { + h := &Notification{ + handler: handler, + userEvents: userEvents, + systemDefaults: defaults, + i18n: translator, + statikDir: statikDir, + AesCrypto: aesCrypto, + } + + h.subscribe() + + return h +} + +func (k *Notification) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (n *Notification) ViewModel() string { return notificationTable } +func (_ *Notification) AggregateTypes() []models.AggregateType { + return []models.AggregateType{es_model.UserAggregate} +} + +func (n *Notification) CurrentSequence(event *models.Event) (uint64, error) { + sequence, err := n.view.GetLatestNotificationSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + func (n *Notification) EventQuery() (*models.SearchQuery, error) { - sequence, err := n.view.GetLatestNotificationSequence() + sequence, err := n.view.GetLatestNotificationSequence("") if err != nil { return nil, err } - return eventsourcing.UserQuery(sequence.CurrentSequence), nil + return usr_event.UserQuery(sequence.CurrentSequence), nil } func (n *Notification) Reduce(event *models.Event) (err error) { @@ -70,13 +111,11 @@ func (n *Notification) Reduce(event *models.Event) (err error) { err = n.handlePasswordCode(event) case es_model.DomainClaimed: err = n.handleDomainClaimed(event) - default: - return n.view.ProcessedNotificationSequence(event.Sequence, event.CreationDate) } if err != nil { return err } - return n.view.ProcessedNotificationSequence(event.Sequence, event.CreationDate) + return n.view.ProcessedNotificationSequence(event) } func (n *Notification) handleInitUserCode(event *models.Event) (err error) { @@ -229,12 +268,12 @@ func (n *Notification) checkIfAlreadyHandled(userID string, sequence uint64, eve } func (n *Notification) getUserEvents(userID string, sequence uint64) ([]*models.Event, error) { - query, err := eventsourcing.UserByIDQuery(userID, sequence) + query, err := usr_event.UserByIDQuery(userID, sequence) if err != nil { return nil, err } - return n.eventstore.FilterEvents(context.Background(), query) + return n.es.FilterEvents(context.Background(), query) } func (n *Notification) OnError(event *models.Event, err error) error { @@ -254,7 +293,7 @@ func getSetNotifyContextData(orgID string) context.Context { func (n *Notification) getLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { // read from Org policy, err := n.view.LabelPolicyByAggregateID(authz.GetCtxData(ctx).OrgID, labelPolicyTableOrg) - if errors.IsNotFound(err) { + if caos_errs.IsNotFound(err) { // read from default policy, err = n.view.LabelPolicyByAggregateID(n.systemDefaults.IamID, labelPolicyTableDef) if err != nil { diff --git a/internal/notification/repository/eventsourcing/handler/notify_user.go b/internal/notification/repository/eventsourcing/handler/notify_user.go index b41cd93ce7..4b82a16c0c 100644 --- a/internal/notification/repository/eventsourcing/handler/notify_user.go +++ b/internal/notification/repository/eventsourcing/handler/notify_user.go @@ -2,14 +2,13 @@ package handler import ( "context" - iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/logging" - "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/query" "github.com/caos/zitadel/internal/eventstore/spooler" + iam_es "github.com/caos/zitadel/internal/iam/repository/eventsourcing" org_model "github.com/caos/zitadel/internal/org/model" org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" @@ -17,33 +16,72 @@ import ( view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) -type NotifyUser struct { - handler - eventstore eventstore.Eventstore - orgEvents *org_events.OrgEventstore - iamEvents *iam_es.IAMEventstore - iamID string -} - const ( userTable = "notification.notify_users" ) +type NotifyUser struct { + handler + orgEvents *org_events.OrgEventstore + iamEvents *iam_es.IAMEventstore + iamID string + subscription *eventstore.Subscription +} + +func newNotifyUser( + handler handler, + orgEvents *org_events.OrgEventstore, + iamEvents *iam_es.IAMEventstore, + iamID string, +) *NotifyUser { + h := &NotifyUser{ + handler: handler, + orgEvents: orgEvents, + iamEvents: iamEvents, + iamID: iamID, + } + + h.subscribe() + + return h +} + +func (k *NotifyUser) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + func (p *NotifyUser) ViewModel() string { return userTable } -func (p *NotifyUser) EventQuery() (*models.SearchQuery, error) { - sequence, err := p.view.GetLatestNotifyUserSequence() +func (_ *NotifyUser) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{es_model.UserAggregate, org_es_model.OrgAggregate} +} + +func (p *NotifyUser) CurrentSequence(event *es_models.Event) (uint64, error) { + sequence, err := p.view.GetLatestNotifyUserSequence(string(event.AggregateType)) + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (p *NotifyUser) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := p.view.GetLatestNotifyUserSequence("") if err != nil { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + AggregateTypeFilter(p.AggregateTypes()...). LatestSequenceFilter(sequence.CurrentSequence), nil } -func (u *NotifyUser) Reduce(event *models.Event) (err error) { +func (u *NotifyUser) Reduce(event *es_models.Event) (err error) { switch event.AggregateType { case es_model.UserAggregate: return u.ProcessUser(event) @@ -54,7 +92,7 @@ func (u *NotifyUser) Reduce(event *models.Event) (err error) { } } -func (u *NotifyUser) ProcessUser(event *models.Event) (err error) { +func (u *NotifyUser) ProcessUser(event *es_models.Event) (err error) { user := new(view_model.NotifyUser) switch event.Type { case es_model.UserAdded, @@ -93,17 +131,17 @@ func (u *NotifyUser) ProcessUser(event *models.Event) (err error) { } u.fillLoginNames(user) case es_model.UserRemoved: - return u.view.DeleteNotifyUser(event.AggregateID, event.Sequence, event.CreationDate) + return u.view.DeleteNotifyUser(event.AggregateID, event) default: - return u.view.ProcessedNotifyUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedNotifyUserSequence(event) } if err != nil { return err } - return u.view.PutNotifyUser(user, user.Sequence, event.CreationDate) + return u.view.PutNotifyUser(user, event) } -func (u *NotifyUser) ProcessOrg(event *models.Event) (err error) { +func (u *NotifyUser) ProcessOrg(event *es_models.Event) (err error) { switch event.Type { case org_es_model.OrgDomainVerified, org_es_model.OrgDomainRemoved, @@ -114,11 +152,11 @@ func (u *NotifyUser) ProcessOrg(event *models.Event) (err error) { case org_es_model.OrgDomainPrimarySet: return u.fillPreferredLoginNamesOnOrgUsers(event) default: - return u.view.ProcessedNotifyUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedNotifyUserSequence(event) } } -func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *models.Event) error { +func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *es_models.Event) error { org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner)) if err != nil { return err @@ -136,15 +174,15 @@ func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *models.Event) error { } for _, user := range users { user.SetLoginNames(policy, org.Domains) - err := u.view.PutNotifyUser(user, 0, event.CreationDate) + err := u.view.PutNotifyUser(user, event) if err != nil { return err } } - return u.view.ProcessedNotifyUserSequence(event.Sequence, event.CreationDate) + return u.view.ProcessedNotifyUserSequence(event) } -func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { +func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error { org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner)) if err != nil { return err @@ -165,7 +203,7 @@ func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *models.Event) erro } for _, user := range users { user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) - err := u.view.PutNotifyUser(user, 0, event.CreationDate) + err := u.view.PutNotifyUser(user, event) if err != nil { return err } @@ -190,7 +228,7 @@ func (u *NotifyUser) fillLoginNames(user *view_model.NotifyUser) (err error) { return nil } -func (p *NotifyUser) OnError(event *models.Event, err error) error { +func (p *NotifyUser) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-9spwf", "id", event.AggregateID).WithError(err).Warn("something went wrong in notify user handler") return spooler.HandleError(event, err, p.view.GetLatestNotifyUserFailedEvent, p.view.ProcessedNotifyUserFailedEvent, p.view.ProcessedNotifyUserSequence, p.errorCountUntilSkip) } diff --git a/internal/notification/repository/eventsourcing/view/notification.go b/internal/notification/repository/eventsourcing/view/notification.go index baeb75e162..b14242f47e 100644 --- a/internal/notification/repository/eventsourcing/view/notification.go +++ b/internal/notification/repository/eventsourcing/view/notification.go @@ -1,20 +1,20 @@ package view import ( + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( notificationTable = "notification.notifications" ) -func (v *View) GetLatestNotificationSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(notificationTable) +func (v *View) GetLatestNotificationSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(notificationTable, aggregateType) } -func (v *View) ProcessedNotificationSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(notificationTable, eventSequence, eventTimestamp) +func (v *View) ProcessedNotificationSequence(event *models.Event) error { + return v.saveCurrentSequence(notificationTable, event) } func (v *View) UpdateNotificationSpoolerRunTimestamp() error { diff --git a/internal/notification/repository/eventsourcing/view/notify_user.go b/internal/notification/repository/eventsourcing/view/notify_user.go index 44d1a1e32b..f81c986d0a 100644 --- a/internal/notification/repository/eventsourcing/view/notify_user.go +++ b/internal/notification/repository/eventsourcing/view/notify_user.go @@ -1,10 +1,11 @@ package view import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/user/repository/view" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" - "time" ) const ( @@ -15,35 +16,32 @@ func (v *View) NotifyUserByID(userID string) (*model.NotifyUser, error) { return view.NotifyUserByID(v.Db, notifyUserTable, userID) } -func (v *View) PutNotifyUser(user *model.NotifyUser, sequence uint64, eventTimestamp time.Time) error { +func (v *View) PutNotifyUser(user *model.NotifyUser, event *models.Event) error { err := view.PutNotifyUser(v.Db, notifyUserTable, user) if err != nil { return err } - if sequence != 0 { - return v.ProcessedNotifyUserSequence(sequence, eventTimestamp) - } - return nil + return v.ProcessedNotifyUserSequence(event) } func (v *View) NotifyUsersByOrgID(orgID string) ([]*model.NotifyUser, error) { return view.NotifyUsersByOrgID(v.Db, notifyUserTable, orgID) } -func (v *View) DeleteNotifyUser(userID string, eventSequence uint64, eventTimestamp time.Time) error { +func (v *View) DeleteNotifyUser(userID string, event *models.Event) error { err := view.DeleteNotifyUser(v.Db, notifyUserTable, userID) - if err != nil { - return nil + if err != nil && !errors.IsNotFound(err) { + return err } - return v.ProcessedNotifyUserSequence(eventSequence, eventTimestamp) + return v.ProcessedNotifyUserSequence(event) } -func (v *View) GetLatestNotifyUserSequence() (*repository.CurrentSequence, error) { - return v.latestSequence(notifyUserTable) +func (v *View) GetLatestNotifyUserSequence(aggregateType string) (*repository.CurrentSequence, error) { + return v.latestSequence(notifyUserTable, aggregateType) } -func (v *View) ProcessedNotifyUserSequence(eventSequence uint64, eventTimestamp time.Time) error { - return v.saveCurrentSequence(notifyUserTable, eventSequence, eventTimestamp) +func (v *View) ProcessedNotifyUserSequence(event *models.Event) error { + return v.saveCurrentSequence(notifyUserTable, event) } func (v *View) UpdateNotifyUserSpoolerRunTimestamp() error { diff --git a/internal/notification/repository/eventsourcing/view/sequence.go b/internal/notification/repository/eventsourcing/view/sequence.go index f1868e4f44..8d56cc19b9 100644 --- a/internal/notification/repository/eventsourcing/view/sequence.go +++ b/internal/notification/repository/eventsourcing/view/sequence.go @@ -1,24 +1,26 @@ package view import ( - "github.com/caos/zitadel/internal/view/repository" "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/view/repository" ) const ( sequencesTable = "notification.current_sequences" ) -func (v *View) saveCurrentSequence(viewName string, sequence uint64, eventTimestamp time.Time) error { - return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence, eventTimestamp) +func (v *View) saveCurrentSequence(viewName string, event *models.Event) error { + return repository.SaveCurrentSequence(v.Db, sequencesTable, viewName, string(event.AggregateType), event.Sequence, event.CreationDate) } -func (v *View) latestSequence(viewName string) (*repository.CurrentSequence, error) { - return repository.LatestSequence(v.Db, sequencesTable, viewName) +func (v *View) latestSequence(viewName, aggregateType string) (*repository.CurrentSequence, error) { + return repository.LatestSequence(v.Db, sequencesTable, viewName, aggregateType) } func (v *View) updateSpoolerRunSequence(viewName string) error { - currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName) + currentSequence, err := repository.LatestSequence(v.Db, sequencesTable, viewName, "") if err != nil { return err } @@ -26,5 +28,8 @@ func (v *View) updateSpoolerRunSequence(viewName string) error { currentSequence.ViewName = viewName } currentSequence.LastSuccessfulSpoolerRun = time.Now() + //update all aggregate types + //TODO: not sure if all scenarios work as expected + currentSequence.AggregateType = "" return repository.UpdateCurrentSequence(v.Db, sequencesTable, currentSequence) } diff --git a/internal/notification/templates/templateData.go b/internal/notification/templates/templateData.go index ee7b910709..676af69d4b 100644 --- a/internal/notification/templates/templateData.go +++ b/internal/notification/templates/templateData.go @@ -24,6 +24,8 @@ func (data *TemplateData) Translate(i18n *i18n.Translator, args map[string]inter data.Subject = i18n.Localize(data.Subject, nil, langs...) data.Greeting = i18n.Localize(data.Greeting, args, langs...) data.Text = html.UnescapeString(i18n.Localize(data.Text, args, langs...)) - data.Href = i18n.Localize(data.Href, nil, langs...) + if data.Href != "" { + data.Href = i18n.Localize(data.Href, nil, langs...) + } data.ButtonText = i18n.Localize(data.ButtonText, nil, langs...) } diff --git a/internal/org/repository/eventsourcing/eventstore_mock_test.go b/internal/org/repository/eventsourcing/eventstore_mock_test.go index 6d7d175317..f36f48d7f9 100644 --- a/internal/org/repository/eventsourcing/eventstore_mock_test.go +++ b/internal/org/repository/eventsourcing/eventstore_mock_test.go @@ -42,7 +42,7 @@ func GetMockChangesOrgOK(ctrl *gomock.Controller) *OrgEventstore { } events := []*es_models.Event{ - {AggregateID: "AggregateIDApp", Sequence: 1, AggregateType: repo_model.OrgAggregate, Data: data}, + {AggregateID: "AggregateID", Sequence: 1, AggregateType: repo_model.OrgAggregate, Data: data}, } mockEs := mock.NewMockEventstore(ctrl) mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) diff --git a/internal/org/repository/eventsourcing/eventstore_test.go b/internal/org/repository/eventsourcing/eventstore_test.go index b0c1430b8b..b1b0a75aa3 100644 --- a/internal/org/repository/eventsourcing/eventstore_test.go +++ b/internal/org/repository/eventsourcing/eventstore_test.go @@ -179,7 +179,7 @@ func TestOrgEventstore_OrgByID(t *testing.T) { { name: "new events found and added success", fields: fields{Eventstore: newTestEventstore(t).expectFilterEvents([]*es_models.Event{ - {Sequence: 6}, + {Sequence: 6, AggregateID: "hodor-org"}, }, nil)}, args: args{ ctx: authz.NewMockContext("user", "org"), diff --git a/internal/org/repository/view/org_member_view.go b/internal/org/repository/view/org_member_view.go index ef52864b52..c9035d38dc 100644 --- a/internal/org/repository/view/org_member_view.go +++ b/internal/org/repository/view/org_member_view.go @@ -17,7 +17,7 @@ func OrgMemberByIDs(db *gorm.DB, table, orgID, userID string) (*model.OrgMemberV query := repository.PrepareGetByQuery(table, orgIDQuery, userIDQuery) err := query(db, member) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-DG1qh", "Errors.Org.MemberNotFound") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-gIaTM", "Errors.Org.MemberNotFound") } return member, err } diff --git a/internal/project/repository/eventsourcing/eventstore.go b/internal/project/repository/eventsourcing/eventstore.go index 8967c81389..81d60dbcf8 100644 --- a/internal/project/repository/eventsourcing/eventstore.go +++ b/internal/project/repository/eventsourcing/eventstore.go @@ -811,12 +811,13 @@ func (es *ProjectEventstore) VerifyOIDCClientSecret(ctx context.Context, project err = crypto.CompareHash(app.OIDCConfig.ClientSecret, []byte(secret), es.passwordAlg) spanHash.EndWithError(err) if err == nil { - return es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckSucceededAggregate) + err = es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckSucceededAggregate) + logging.Log("EVENT-AE1vf").OnError(err).Warn("could not push event OIDCClientSecretCheckSucceeded") + return nil } - if err := es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckFailedAggregate); err != nil { - return err - } - return caos_errs.ThrowInvalidArgument(nil, "EVENT-wg24q", "Errors.Internal") + err = es.setOIDCClientSecretCheckResult(ctx, existingProject, app.AppID, OIDCClientSecretCheckFailedAggregate) + logging.Log("EVENT-GD1gh").OnError(err).Warn("could not push event OIDCClientSecretCheckFailed") + return caos_errs.ThrowInvalidArgument(nil, "EVENT-wg24q", "Errors.Project.OIDCSecretInvalid") } func (es *ProjectEventstore) setOIDCClientSecretCheckResult(ctx context.Context, project *proj_model.Project, appID string, check func(*es_models.AggregateCreator, *model.Project, string) es_sdk.AggregateFunc) error { diff --git a/internal/project/repository/view/application_view.go b/internal/project/repository/view/application_view.go index 414d7dfcf7..2abfd9f47d 100644 --- a/internal/project/repository/view/application_view.go +++ b/internal/project/repository/view/application_view.go @@ -24,7 +24,7 @@ func ApplicationByID(db *gorm.DB, table, projectID, appID string) (*model.Applic func ApplicationsByProjectID(db *gorm.DB, table, projectID string) ([]*model.ApplicationView, error) { applications := make([]*model.ApplicationView, 0) queries := []*proj_model.ApplicationSearchQuery{ - &proj_model.ApplicationSearchQuery{Key: proj_model.AppSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.AppSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ApplicationSearchRequest{Queries: queries}) _, err := query(db, &applications) diff --git a/internal/project/repository/view/project_role_view.go b/internal/project/repository/view/project_role_view.go index 12248b6640..4065439d46 100644 --- a/internal/project/repository/view/project_role_view.go +++ b/internal/project/repository/view/project_role_view.go @@ -26,7 +26,7 @@ func ProjectRoleByIDs(db *gorm.DB, table, projectID, orgID, key string) (*model. func ProjectRolesByProjectID(db *gorm.DB, table, projectID string) ([]*model.ProjectRoleView, error) { roles := make([]*model.ProjectRoleView, 0) queries := []*proj_model.ProjectRoleSearchQuery{ - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectRoleSearchRequest{Queries: queries}) _, err := query(db, &roles) @@ -39,9 +39,9 @@ func ProjectRolesByProjectID(db *gorm.DB, table, projectID string) ([]*model.Pro func ResourceOwnerProjectRolesByKey(db *gorm.DB, table, projectID, resourceOwner, key string) ([]*model.ProjectRoleView, error) { roles := make([]*model.ProjectRoleView, 0) queries := []*proj_model.ProjectRoleSearchQuery{ - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyKey, Value: key, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyKey, Value: key, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectRoleSearchRequest{Queries: queries}) _, err := query(db, &roles) @@ -54,8 +54,8 @@ func ResourceOwnerProjectRolesByKey(db *gorm.DB, table, projectID, resourceOwner func ResourceOwnerProjectRoles(db *gorm.DB, table, projectID, resourceOwner string) ([]*model.ProjectRoleView, error) { roles := make([]*model.ProjectRoleView, 0) queries := []*proj_model.ProjectRoleSearchQuery{ - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, - &proj_model.ProjectRoleSearchQuery{Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectRoleSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectRoleSearchRequest{Queries: queries}) _, err := query(db, &roles) diff --git a/internal/setup/config.go b/internal/setup/config.go index be3e19099a..01143e7af1 100644 --- a/internal/setup/config.go +++ b/internal/setup/config.go @@ -14,6 +14,7 @@ type IAMSetUp struct { //Step6 *Step6 //Step7 *Step7 //Step8 *Step8 + //Step9 *Step9 } func (setup *IAMSetUp) steps(currentDone iam_model.Step) ([]stepV2, error) { @@ -29,6 +30,7 @@ func (setup *IAMSetUp) steps(currentDone iam_model.Step) ([]stepV2, error) { //setup.Step6, //setup.Step7, //setup.Step8, + //setup.Step9, } { if step.step() <= currentDone { continue diff --git a/internal/setup/step9.go b/internal/setup/step9.go new file mode 100644 index 0000000000..2733dbb51a --- /dev/null +++ b/internal/setup/step9.go @@ -0,0 +1,74 @@ +package setup + +import ( + "context" + + "github.com/caos/logging" + + "github.com/caos/zitadel/internal/eventstore/models" + es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" + iam_model "github.com/caos/zitadel/internal/iam/model" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" +) + +type Step9 struct { + Passwordless bool + + setup *Setup +} + +func (step *Step9) isNil() bool { + return step == nil +} + +func (step *Step9) step() iam_model.Step { + return iam_model.Step9 +} + +func (step *Step9) init(setup *Setup) { + step.setup = setup +} + +func (step *Step9) execute(ctx context.Context) (*iam_model.IAM, error) { + if !step.Passwordless { + return step.setup.IamEvents.IAMByID(ctx, step.setup.iamID) + } + iam, agg, err := step.setPasswordlessAllowedInPolicy(ctx) + if err != nil { + logging.Log("SETUP-Gdbjq").WithField("step", step.step()).WithError(err).Error("unable to finish setup (add default mfa to login policy)") + return nil, err + } + iam, agg2, err := step.addMFAToPolicy(ctx) + if err != nil { + logging.Log("SETUP-Gdbjq").WithField("step", step.step()).WithError(err).Error("unable to finish setup (add default mfa to login policy)") + return nil, err + } + agg.Events = append(agg.Events, agg2.Events...) + iam, agg, push, err := step.setup.IamEvents.PrepareSetupDone(ctx, iam, agg, step.step()) + if err != nil { + logging.Log("SETUP-Cnf21").WithField("step", step.step()).WithError(err).Error("unable to finish setup (prepare setup done)") + return nil, err + } + err = es_sdk.PushAggregates(ctx, push, iam.AppendEvents, agg) + if err != nil { + logging.Log("SETUP-NFq21").WithField("step", step.step()).WithError(err).Error("unable to finish setup") + return nil, err + } + return iam_es_model.IAMToModel(iam), nil +} + +func (step *Step9) setPasswordlessAllowedInPolicy(ctx context.Context) (*iam_es_model.IAM, *models.Aggregate, error) { + logging.Log("SETUP-DAd1h").Info("enabling passwordless in loginPolicy") + iam, err := step.setup.IamEvents.IAMByID(ctx, step.setup.iamID) + if err != nil { + return nil, nil, err + } + iam.DefaultLoginPolicy.AggregateID = step.setup.iamID + iam.DefaultLoginPolicy.PasswordlessType = iam_model.PasswordlessTypeAllowed + return step.setup.IamEvents.PrepareChangeLoginPolicy(ctx, iam.DefaultLoginPolicy) +} + +func (step *Step9) addMFAToPolicy(ctx context.Context) (*iam_es_model.IAM, *models.Aggregate, error) { + logging.Log("SETUP-DAd1h").Info("adding MFA to loginPolicy") + return step.setup.IamEvents.PrepareAddMultiFactorToLoginPolicy(ctx, step.setup.iamID, iam_model.MultiFactorTypeU2FWithPIN) +} diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 1749bdc67c..41d3b104bf 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -4,6 +4,8 @@ Errors: OriginNotAllowed: Dieser "Origin" ist nicht freigeschaltet User: NotFound: Benutzer konnte nicht gefunden werden + NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden + NotAllowedOrg: Benutzer gehört nicht der benötigten Organisation an UserIDMissing: User ID fehlt OrgIamPolicyNil: Organisations Policy ist leer EmailAsUsernameNotAllowed: Benutzername darf keine E-Mail Adresse sein @@ -169,6 +171,7 @@ Errors: GrantNotExists: Projekt Grant existiert nicht GrantHasNotExistingRole: Eine der Rollen existiert nicht auf dem Projekt UserIDMisisng: User ID fehlt + OIDCSecretInvalid: Client Secret ist ungültig IAM: Member: RolesNotChanged: Rollen wurden nicht verändert @@ -466,9 +469,16 @@ EventTypes: login: added: Login Richtlinie hinzugefügt changed: Login Richtlinie geändert + removed: Login Richtline gelöscht idpprovider: added: Idp Provider zu Login Richtlinie hinzugefügt removed: Idp Provider aus Login Richtlinie gelöscht + secondfactor: + added: Zweitfaktor zu Login Richtlinie hinzugefügt + removed: Zweitfaktor aus Login Richtlinie gelöscht + multifactor: + added: Multifaktor zu Login Richtlinie hinzugefügt + removed: Multifaktor aus Login Richtlinie gelöscht password: complexity: added: Passwort Komplexitäts Richtlinie hinzugefügt diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 6ae31d5862..d2a7c1f4bb 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -4,6 +4,8 @@ Errors: OriginNotAllowed: This "Origin" is not allowed User: NotFound: User could not be found + NotFoundOnOrg: User could not be found on chosen organisation + NotAllowedOrg: User is no member of the required organisation UserIDMissing: User ID missing OrgIamPolicyNil: Organisation Policy is empty EmailAsUsernameNotAllowed: Email is not allowed as username @@ -169,6 +171,7 @@ Errors: GrantNotExists: Project grant doesn't exist GrantHasNotExistingRole: One role doesn't exist on project UserIDMisisng: User ID missing + OIDCSecretInvalid: Client Secret is invalid IAM: Member: RolesNotChanged: Roles habe not been changed @@ -463,9 +466,16 @@ EventTypes: login: added: Login Policy added changed: Login Policy changed + removed: Login Policy removed idpprovider: added: Idp Provider added to Login Policy removed: Idp Provider removed from Login Policy + secondfactor: + added: Second factor added to Login Policy + removed: Second factor removed from Login Policy + multifactor: + added: Multi factor added to Login Policy + removed: Multi factor removed from Login Policy password: complexity: added: Password complexity policy added diff --git a/internal/ui/login/handler/external_login_handler.go b/internal/ui/login/handler/external_login_handler.go index 2bb2d0d6e7..1d7455e91f 100644 --- a/internal/ui/login/handler/external_login_handler.go +++ b/internal/ui/login/handler/external_login_handler.go @@ -188,16 +188,10 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID}, Roles: []string{orgProjectCreatorRole}, } - if authReq.GetScopeOrgPrimaryDomain() != "" { - primaryDomain := authReq.GetScopeOrgPrimaryDomain() - org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, err) - } - if org.ID != iam.GlobalOrgID { - member = nil - resourceOwner = org.ID - } + + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + member = nil + resourceOwner = authReq.RequestedOrgID } orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner) diff --git a/internal/ui/login/handler/external_register_handler.go b/internal/ui/login/handler/external_register_handler.go index 0d8079adeb..3d2834bb47 100644 --- a/internal/ui/login/handler/external_register_handler.go +++ b/internal/ui/login/handler/external_register_handler.go @@ -85,17 +85,9 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques Roles: []string{orgProjectCreatorRole}, } - if authReq.GetScopeOrgPrimaryDomain() != "" { - primaryDomain := authReq.GetScopeOrgPrimaryDomain() - org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - l.renderRegisterOption(w, r, authReq, err) - return - } - if org.ID != iam.GlobalOrgID { - member = nil - resourceOwner = org.ID - } + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + member = nil + resourceOwner = authReq.RequestedOrgID } orgIamPolicy, err := l.getOrgIamPolicy(r, resourceOwner) if err != nil { diff --git a/internal/ui/login/handler/mfa_verify_handler.go b/internal/ui/login/handler/mfa_verify_handler.go index 5e3b5c5a8f..3d284e01aa 100644 --- a/internal/ui/login/handler/mfa_verify_handler.go +++ b/internal/ui/login/handler/mfa_verify_handler.go @@ -60,6 +60,9 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request, case model.MFATypeOTP: data.MFAProviders = removeSelectedProviderFromList(verificationStep.MFAProviders, model.MFATypeOTP) data.SelectedMFAProvider = model.MFATypeOTP + default: + l.renderError(w, r, authReq, err) + return } l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplMFAVerify], data, nil) } diff --git a/internal/ui/login/handler/passwordless_login_handler.go b/internal/ui/login/handler/passwordless_login_handler.go index ee0ae68579..7523e3d1f6 100644 --- a/internal/ui/login/handler/passwordless_login_handler.go +++ b/internal/ui/login/handler/passwordless_login_handler.go @@ -13,6 +13,16 @@ const ( tmplPasswordlessVerification = "passwordlessverification" ) +type passwordlessData struct { + webAuthNData + PasswordLogin bool +} + +type passwordlessFormData struct { + webAuthNFormData + PasswordLogin bool `schema:"passwordlogin"` +} + func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { var errType, errMessage, credentialData string var webAuthNLogin *user_model.WebAuthNLogin @@ -26,20 +36,31 @@ func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Re if webAuthNLogin != nil { credentialData = base64.RawURLEncoding.EncodeToString(webAuthNLogin.CredentialAssertionData) } - data := &webAuthNData{ - userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage), - CredentialCreationData: credentialData, + var passwordLogin bool + if authReq.LoginPolicy != nil { + passwordLogin = authReq.LoginPolicy.AllowUsernamePassword + } + data := &passwordlessData{ + webAuthNData{ + userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage), + CredentialCreationData: credentialData, + }, + passwordLogin, } l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplPasswordlessVerification], data, nil) } func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Request) { - formData := new(webAuthNFormData) + formData := new(passwordlessFormData) authReq, err := l.getAuthRequestAndParseData(r, formData) if err != nil { l.renderError(w, r, authReq, err) return } + if formData.PasswordLogin { + l.renderPassword(w, r, authReq, nil) + return + } credData, err := base64.URLEncoding.DecodeString(formData.CredentialData) if err != nil { l.renderPasswordlessVerification(w, r, authReq, err) diff --git a/internal/ui/login/handler/register_handler.go b/internal/ui/login/handler/register_handler.go index 4ec4eb1bf8..ffb9d91731 100644 --- a/internal/ui/login/handler/register_handler.go +++ b/internal/ui/login/handler/register_handler.go @@ -1,9 +1,10 @@ package handler import ( - "golang.org/x/text/language" "net/http" + "golang.org/x/text/language" + "github.com/caos/zitadel/internal/auth_request/model" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" @@ -71,17 +72,9 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { ObjectRoot: models.ObjectRoot{AggregateID: iam.GlobalOrgID}, Roles: []string{orgProjectCreatorRole}, } - if authRequest.GetScopeOrgPrimaryDomain() != "" { - primaryDomain := authRequest.GetScopeOrgPrimaryDomain() - org, err := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if err != nil { - l.renderRegisterOption(w, r, authRequest, err) - return - } - if org.ID != iam.GlobalOrgID { - member = nil - resourceOwner = org.ID - } + if authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != iam.GlobalOrgID { + member = nil + resourceOwner = authRequest.RequestedOrgID } user, err := l.authRepo.Register(setContext(r.Context(), resourceOwner), data.toUserModel(), member, resourceOwner) if err != nil { diff --git a/internal/ui/login/handler/register_org_handler.go b/internal/ui/login/handler/register_org_handler.go index c1a4b8dc6e..ea2f7a739d 100644 --- a/internal/ui/login/handler/register_org_handler.go +++ b/internal/ui/login/handler/register_org_handler.go @@ -1,9 +1,9 @@ package handler import ( - auth_model "github.com/caos/zitadel/internal/auth/model" "net/http" + auth_model "github.com/caos/zitadel/internal/auth/model" "github.com/caos/zitadel/internal/auth_request/model" caos_errs "github.com/caos/zitadel/internal/errors" org_model "github.com/caos/zitadel/internal/org/model" @@ -15,14 +15,14 @@ const ( ) type registerOrgFormData struct { - OrgName string `schema:"orgname"` - Email string `schema:"email"` - Username string `schema:"username"` - Firstname string `schema:"firstname"` - Lastname string `schema:"lastname"` - Password string `schema:"register-password"` - Password2 string `schema:"register-password-confirmation"` - TermsConfirm bool `schema:"terms-confirm"` + RegisterOrgName string `schema:"orgname"` + Email string `schema:"email"` + Username string `schema:"username"` + Firstname string `schema:"firstname"` + Lastname string `schema:"lastname"` + Password string `schema:"register-password"` + Password2 string `schema:"register-password-confirmation"` + TermsConfirm bool `schema:"terms-confirm"` } type registerOrgData struct { @@ -140,6 +140,6 @@ func (d registerOrgFormData) toUserModel() *usr_model.User { func (d registerOrgFormData) toOrgModel() *org_model.Org { return &org_model.Org{ - Name: d.OrgName, + Name: d.RegisterOrgName, } } diff --git a/internal/ui/login/handler/renderer.go b/internal/ui/login/handler/renderer.go index dad96049cb..6dbf8d2dce 100644 --- a/internal/ui/login/handler/renderer.go +++ b/internal/ui/login/handler/renderer.go @@ -267,6 +267,7 @@ func (l *Login) getBaseData(r *http.Request, authReq *model.AuthRequest, title s Theme: l.getTheme(r), ThemeMode: l.getThemeMode(r), OrgID: l.getOrgID(authReq), + OrgName: l.getOrgName(authReq), AuthReqID: getRequestID(authReq, r), CSRF: csrf.TemplateField(r), Nonce: http_mw.GetNonce(r), @@ -312,20 +313,17 @@ func (l *Login) getOrgID(authReq *model.AuthRequest) string { if authReq == nil { return "" } - if authReq.UserOrgID != "" { - return authReq.UserOrgID + if authReq.RequestedOrgID != "" { + return authReq.RequestedOrgID } - if authReq.Request == nil { + return authReq.UserOrgID +} + +func (l *Login) getOrgName(authReq *model.AuthRequest) string { + if authReq == nil { return "" } - primaryDomain := authReq.GetScopeOrgPrimaryDomain() - if primaryDomain != "" { - org, _ := l.authRepo.GetOrgByPrimaryDomain(primaryDomain) - if org != nil { - return org.ID - } - } - return "" + return authReq.RequestedOrgName } func getRequestID(authReq *model.AuthRequest, r *http.Request) string { @@ -355,6 +353,7 @@ type baseData struct { Theme string ThemeMode string OrgID string + OrgName string AuthReqID string CSRF template.HTML Nonce string diff --git a/internal/ui/login/static/i18n/de.yaml b/internal/ui/login/static/i18n/de.yaml index d0a303da13..fc86aaa0ad 100644 --- a/internal/ui/login/static/i18n/de.yaml +++ b/internal/ui/login/static/i18n/de.yaml @@ -16,6 +16,7 @@ Login: Loginname: Loginname LoginnamePlaceHolder: username@domain ExternalLogin: Melde dich mit einem externen Benutzer an + MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören. UserSelection: Title: Account auswählen @@ -25,6 +26,7 @@ UserSelection: OtherUser: Anderer Benutzer SessionState0: aktiv SessionState1: inaktiv + MustBeMemberOfOrg: Der Benutzer muss der Organisation {{.OrgName}} angehören. UsernameChange: Title: Usernamen ändern @@ -207,6 +209,7 @@ Actions: RegisterToken: Token registrieren ValidateToken: Token validieren Recreate: erneut erstellen + PasswordLogin: Mit Passwort anmelden Errors: Internal: Es ist ein interner Fehler aufgetreten @@ -216,6 +219,8 @@ Errors: RequestTypeNotSupported: Requesttyp wird nicht unterstürzt User: NotFound: Benutzer konnte nicht gefunden werden + NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden + NotAllowedOrg: Benutzer gehört nicht der benötigten Organisation an NotMatchingUserID: User stimm nicht mit User in Auth Request überein UserIDMissing: UserID ist leer Invalid: Userdaten sind ungültig diff --git a/internal/ui/login/static/i18n/en.yaml b/internal/ui/login/static/i18n/en.yaml index a66f72f465..73ae3d217a 100644 --- a/internal/ui/login/static/i18n/en.yaml +++ b/internal/ui/login/static/i18n/en.yaml @@ -6,6 +6,7 @@ Login: Loginname: Loginname LoginnamePlaceHolder: username@domain ExternalLogin: Login with an external user. + MustBeMemberOfOrg: The user must be mermber of the {{.OrgDomain}} organisation. UserSelection: Title: Select account @@ -15,6 +16,7 @@ UserSelection: OtherUser: Other User SessionState0: active SessionState1: inactive + MustBeMemberOfOrg: The user must be mermber of the {{.OrgDomain}} organisation. Password: Title: Password @@ -207,6 +209,7 @@ Actions: RegisterToken: Register Token ValidateToken: Validate Token Recreate: recreate + PasswordLogin: Login with password Errors: Internal: An internal error occured @@ -216,6 +219,8 @@ Errors: RequestTypeNotSupported: Request type is not supported User: NotFound: User could not be found + NotFoundOnOrg: User could not be found on chosen organisation + NotAllowedOrg: User is no member of the required organisation NotMatchingUserID: User and user in authrequest don't match UserIDMissing: UserID is empty Invalid: Invalid userdata diff --git a/internal/ui/login/static/resources/themes/caos/css/dark.css b/internal/ui/login/static/resources/themes/caos/css/dark.css index 49c8e83bed..7506180196 100644 --- a/internal/ui/login/static/resources/themes/caos/css/dark.css +++ b/internal/ui/login/static/resources/themes/caos/css/dark.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -227,6 +226,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -334,10 +334,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -360,7 +356,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -368,6 +364,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/caos/css/dark.css.map b/internal/ui/login/static/resources/themes/caos/css/dark.css.map index 2e58686f16..5c2f220cfe 100644 --- a/internal/ui/login/static/resources/themes/caos/css/dark.css.map +++ b/internal/ui/login/static/resources/themes/caos/css/dark.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCNc;EDOd,OCNQ;EDOR;EACA;EACA;;;AAMJ;EACI,OChBQ;EDiBR,aCvBS;EDwBT;EACA,WE9BS;EF+BT;;;AAGJ;EACI,OCxBQ;EDyBR,aC/BS;EDgCT;EACA,WErCU;;;AFwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC5DW;ED6DX;EACA;;AAEA;EACI,OChEY;;ADmEhB;EACI;;;AAIR;EACI,kBC5Ec;ED6Ed,OC3EW;ED4EX;EACA;EACA;EACA;EACA,QE7FU;EF8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCzFY;ED0FZ,OC7FU;ED8FV;;AAGJ;EACI,kBChGO;EDiGP,OClGI;EDmGJ;;AACA;EACI,kBCnGQ;;ADuGhB;EACI,kBE7FW;EF8FX;;AAEA;EACI,kBEjGO;EFkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OErGa;EFsGb,kBErGmB;;AFuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBEnImB;EFoInB,OClJQ;EDmJR,QE/JU;EFgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EE9JN;;AACA;EFyJE;IExJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEtRO;EFuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OElTP;;AFyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE5UV;;AACA;EFuUM;IEtUJ;IACA;;;AF8UQ;EACI;EACA;EEnVd;;AACA;EFgVU;IE/UR;IACA;;;AFqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE1VN;;AF+VE;EACI,OEjWL;;AFsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC7YI;;ADgZR;EACI,MClZU;;;ADuZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OEjbO;;;AFobX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCLc;EDMd,OCLQ;EDMR;EACA;EACA;;;AAMJ;EACI,OCfQ;EDgBR,aCtBS;EDuBT;EACA,WE7BS;EF8BT;;;AAGJ;EACI,OCvBQ;EDwBR,aC9BS;ED+BT;EACA,WEpCU;;;AFuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC3DW;ED4DX;EACA;;AAEA;EACI,OC/DY;;ADkEhB;EACI;;;AAIR;EACI,kBC3Ec;ED4Ed,OC1EW;ED2EX;EACA;EACA;EACA;EACA,QE5FU;EF6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCxFY;EDyFZ,OC5FU;ED6FV;;AAGJ;EACI,kBC/FO;EDgGP,OCjGI;EDkGJ;;AACA;EACI,kBClGQ;;ADsGhB;EACI,kBE5FW;EF6FX;;AAEA;EACI,kBEhGO;EFiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OEpGa;EFqGb,kBEpGmB;;AFsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBElImB;EFmInB,OCjJQ;EDkJR,QE9JU;EF+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EE9JN;;AACA;EFwJE;IEvJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cElRO;EFmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OE9SP;;AFmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBC1UE;;AD8UF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE7VV;;AACA;EFwVM;IEvVJ;IACA;;;AF+VQ;EACI;EACA;EEpWd;;AACA;EFiWU;IEhWR;IACA;;;AFsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE3WN;;AFgXE;EACI,OElXL;;AFuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC9ZI;;ADiaR;EACI,MCnaU;;;ADwad;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OElcO;;;AFqcX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/caos/css/light.css b/internal/ui/login/static/resources/themes/caos/css/light.css index c4cd152ea1..1d905b2969 100644 --- a/internal/ui/login/static/resources/themes/caos/css/light.css +++ b/internal/ui/login/static/resources/themes/caos/css/light.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -227,6 +226,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -334,10 +334,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -360,7 +356,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -368,6 +364,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/caos/css/light.css.map b/internal/ui/login/static/resources/themes/caos/css/light.css.map index 6c3f74693e..18d1ec38a3 100644 --- a/internal/ui/login/static/resources/themes/caos/css/light.css.map +++ b/internal/ui/login/static/resources/themes/caos/css/light.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCNc;EDOd,OCNQ;EDOR;EACA;EACA;;;AAMJ;EACI,OChBQ;EDiBR,aCvBS;EDwBT;EACA,WE9BS;EF+BT;;;AAGJ;EACI,OCxBQ;EDyBR,aC/BS;EDgCT;EACA,WErCU;;;AFwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC5DW;ED6DX;EACA;;AAEA;EACI,OChEY;;ADmEhB;EACI;;;AAIR;EACI,kBC5Ec;ED6Ed,OC3EW;ED4EX;EACA;EACA;EACA;EACA,QE7FU;EF8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCzFY;ED0FZ,OC7FU;ED8FV;;AAGJ;EACI,kBChGO;EDiGP,OClGI;EDmGJ;;AACA;EACI,kBCnGQ;;ADuGhB;EACI,kBE7FW;EF8FX;;AAEA;EACI,kBEjGO;EFkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OErGa;EFsGb,kBErGmB;;AFuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBEnImB;EFoInB,OClJQ;EDmJR,QE/JU;EFgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EE9JN;;AACA;EFyJE;IExJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cEtRO;EFuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OElTP;;AFyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE5UV;;AACA;EFuUM;IEtUJ;IACA;;;AF8UQ;EACI;EACA;EEnVd;;AACA;EFgVU;IE/UR;IACA;;;AFqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE1VN;;AF+VE;EACI,OEjWL;;AFsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC7YI;;ADgZR;EACI,MClZU;;;ADuZd;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OEjbO;;;AFobX;EACI;;;AAGJ;EACI;;;AG9dJ;EACI,kBFeQ;EEdR,OFac;;AERd;EACI;;AAGJ;EACI,OFGU;;AEAd;EACI;EACA;EACA;;AAEA;EACI,kBFIa;EEHb;EACA,ODyBgB;;ACtBpB;EACI,kBFVG;EEWH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBFdI;;AEkBZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,OFhCM;;AEkCN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,OF9EU;;AEkFV;EACI,MFnFM;;AEsFV;EACI,MFtFA;;AE0FR;EAEQ;;;AAMR;EACI,OFpGU;;AEwGb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/caos/variables.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCMW;EDLX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCLc;EDMd,OCLQ;EDMR;EACA;EACA;;;AAMJ;EACI,OCfQ;EDgBR,aCtBS;EDuBT;EACA,WE7BS;EF8BT;;;AAGJ;EACI,OCvBQ;EDwBR,aC9BS;ED+BT;EACA,WEpCU;;;AFuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OC3DW;ED4DX;EACA;;AAEA;EACI,OC/DY;;ADkEhB;EACI;;;AAIR;EACI,kBC3Ec;ED4Ed,OC1EW;ED2EX;EACA;EACA;EACA;EACA,QE5FU;EF6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBCxFY;EDyFZ,OC5FU;ED6FV;;AAGJ;EACI,kBC/FO;EDgGP,OCjGI;EDkGJ;;AACA;EACI,kBClGQ;;ADsGhB;EACI,kBE5FW;EF6FX;;AAEA;EACI,kBEhGO;EFiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OEpGa;EFqGb,kBEpGmB;;AFsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBElImB;EFmInB,OCjJQ;EDkJR,QE9JU;EF+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EE9JN;;AACA;EFwJE;IEvJA;IACA;;;AF+JA;EElKF;;AACA;EFiKE;IEhKA;IACA;;;;AFsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WE5LE;EF6LF;;AAGJ;EACI;EACA;EACA;EACA,OE/KC;;;AFqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OCpOA;;ADwOR;EACI,OE7NK;EF8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OCnQI;EDoQJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBE7PW;;AFgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cElRO;EFmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OE9SP;;AFmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBC1UE;;AD8UF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EE7VV;;AACA;EFwVM;IEvVJ;IACA;;;AF+VQ;EACI;EACA;EEpWd;;AACA;EFiWU;IEhWR;IACA;;;AFsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OE3WN;;AFgXE;EACI,OElXL;;AFuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MC9ZI;;ADiaR;EACI,MCnaU;;;ADwad;EACI;EACA;;;AAIR;EAEQ;EAEJ;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OElcO;;;AFqcX;EACI;;;AAGJ;EACI;;;AG/eJ;EACI,kBFeQ;EEdR,OFac;;AERd;EACI;;AAGJ;EACI,OFGU;;AEAd;EACI;EACA;EACA;;AAEA;EACI,kBFIa;EEHb;EACA,ODyBgB;;ACtBpB;EACI,kBFVG;EEWH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBFdI;;AEkBZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,OFhCM;;AEkCN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,OF9EU;;AEkFV;EACI,MFnFM;;AEsFV;EACI,MFtFA;;AE0FR;EAEQ;;;AAMR;EACI,OFpGU;;AEwGb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/scss/main.scss b/internal/ui/login/static/resources/themes/scss/main.scss index 137c9081ce..30296adb94 100644 --- a/internal/ui/login/static/resources/themes/scss/main.scss +++ b/internal/ui/login/static/resources/themes/scss/main.scss @@ -5,7 +5,6 @@ font-family: $standardFont; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -174,6 +173,7 @@ input:not([type='radio']), select { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; @include retina-background-image($profileImgDark, "png", false, 80px, 80px); } @@ -296,10 +296,6 @@ form { button.user-selection { @extend %profile-image; - .profile-image { - height: 80px; - width: 80px; - } .sessionstate { display: inline-block; @@ -327,7 +323,7 @@ form { position: relative; &.names { - margin: 15px; + margin: 0 15px; .displayname { font-size: 1.4rem; @@ -337,6 +333,27 @@ form { } } } + + &:disabled { + background: transparent; + border: none; + cursor: not-allowed; + + .profile-image { + opacity: 0.3; + } + + .sessionstate { + background-color: $backgroundColor; + } + + .names { + .displayname, .loginname { + font-style: italic; + color: #444444; + } + } + } } button.other-user { diff --git a/internal/ui/login/static/resources/themes/zitadel/css/dark.css b/internal/ui/login/static/resources/themes/zitadel/css/dark.css index f289f43bc1..e9f2328c89 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/dark.css +++ b/internal/ui/login/static/resources/themes/zitadel/css/dark.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -228,6 +227,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -335,10 +335,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -361,7 +357,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -369,6 +365,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map b/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map index bab7b8d3dc..7bb0985450 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map +++ b/internal/ui/login/static/resources/themes/zitadel/css/dark.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCGc;EDFd,OCGQ;EDFR;EACA;EACA;EAEI;;;AAIR;EACI,OCPQ;EDQR,aChCS;EDiCT;EACA,WC9BS;ED+BT;;;AAGJ;EACI,OCfQ;EDgBR,aCxCS;EDyCT;EACA,WCrCU;;;ADwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCnDW;EDoDX;EACA;;AAEA;EACI,OCvDY;;AD0DhB;EACI;;;AAIR;EACI,kBCnEc;EDoEd,OClEW;EDmEX;EACA;EACA;EACA;EACA,QC7FU;ED8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBChFY;EDiFZ,OCpFU;EDqFV;;AAGJ;EACI,kBCvFO;EDwFP,OCzFI;ED0FJ;;AACA;EACI,kBC1FQ;;AD8FhB;EACI,kBC7FW;ED8FX;;AAEA;EACI,kBCjGO;EDkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCrGa;EDsGb,kBCrGmB;;ADuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBCnImB;EDoInB,OCzIQ;ED0IR,QC/JU;EDgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EC9JN;;AACA;EDyJE;ICxJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCtRO;EDuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OClTP;;ADyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC5UV;;AACA;EDuUM;ICtUJ;IACA;;;AD8UQ;EACI;EACA;ECnVd;;AACA;EDgVU;IC/UR;IACA;;;ADqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC1VN;;AD+VE;EACI,OCjWL;;ADsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCpYI;;ADuYR;EACI,MCzYU;;;AD8Yd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OCjbO;;;ADobX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCIc;EDHd,OCIQ;EDHR;EACA;EACA;EAEI;;;AAIR;EACI,OCNQ;EDOR,aC/BS;EDgCT;EACA,WC7BS;ED8BT;;;AAGJ;EACI,OCdQ;EDeR,aCvCS;EDwCT;EACA,WCpCU;;;ADuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OClDW;EDmDX;EACA;;AAEA;EACI,OCtDY;;ADyDhB;EACI;;;AAIR;EACI,kBClEc;EDmEd,OCjEW;EDkEX;EACA;EACA;EACA;EACA,QC5FU;ED6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBC/EY;EDgFZ,OCnFU;EDoFV;;AAGJ;EACI,kBCtFO;EDuFP,OCxFI;EDyFJ;;AACA;EACI,kBCzFQ;;AD6FhB;EACI,kBC5FW;ED6FX;;AAEA;EACI,kBChGO;EDiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCpGa;EDqGb,kBCpGmB;;ADsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBClImB;EDmInB,OCxIQ;EDyIR,QC9JU;ED+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EC9JN;;AACA;EDwJE;ICvJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cClRO;EDmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OC9SP;;ADmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBCjUE;;ADqUF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC7VV;;AACA;EDwVM;ICvVJ;IACA;;;AD+VQ;EACI;EACA;ECpWd;;AACA;EDiWU;IChWR;IACA;;;ADsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC3WN;;ADgXE;EACI,OClXL;;ADuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCrZI;;ADwZR;EACI,MC1ZU;;;AD+Zd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OClcO;;;ADqcX;EACI;;;AAGJ;EACI","file":"dark.css"} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/zitadel/css/light.css b/internal/ui/login/static/resources/themes/zitadel/css/light.css index eae167bc73..26432267ba 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/light.css +++ b/internal/ui/login/static/resources/themes/zitadel/css/light.css @@ -75,7 +75,6 @@ font-family: Lato; font-size: 16px; font-weight: 400; - cursor: default !important; } body { @@ -228,6 +227,7 @@ form button.user-selection .profile-image, .login-profile .profile-image { width: 80px; background-position: center; background-repeat: no-repeat; + background-size: contain; background-image: url("../../../images/icon-user-dark.png"); } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { @@ -335,10 +335,6 @@ form button.clean * { form .user-selection-list { margin-bottom: 40px; } -form button.user-selection .profile-image { - height: 80px; - width: 80px; -} form button.user-selection .sessionstate { display: inline-block; height: 20px; @@ -361,7 +357,7 @@ form button.user-selection > div { position: relative; } form button.user-selection > div.names { - margin: 15px; + margin: 0 15px; } form button.user-selection > div.names .displayname { font-size: 1.4rem; @@ -369,6 +365,21 @@ form button.user-selection > div.names .displayname { form button.user-selection > div.names .loginname { color: #898989; } +form button.user-selection:disabled { + background: transparent; + border: none; + cursor: not-allowed; +} +form button.user-selection:disabled .profile-image { + opacity: 0.3; +} +form button.user-selection:disabled .sessionstate { + background-color: #282828; +} +form button.user-selection:disabled .names .displayname, form button.user-selection:disabled .names .loginname { + font-style: italic; + color: #444444; +} .user-selection + form button.other-user { margin-top: 80px; } diff --git a/internal/ui/login/static/resources/themes/zitadel/css/light.css.map b/internal/ui/login/static/resources/themes/zitadel/css/light.css.map index b7dfa7f778..ab57d44c72 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/light.css.map +++ b/internal/ui/login/static/resources/themes/zitadel/css/light.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCGc;EDFd,OCGQ;EDFR;EACA;EACA;EAEI;;;AAIR;EACI,OCPQ;EDQR,aChCS;EDiCT;EACA,WC9BS;ED+BT;;;AAGJ;EACI,OCfQ;EDgBR,aCxCS;EDyCT;EACA,WCrCU;;;ADwCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OCnDW;EDoDX;EACA;;AAEA;EACI,OCvDY;;AD0DhB;EACI;;;AAIR;EACI,kBCnEc;EDoEd,OClEW;EDmEX;EACA;EACA;EACA;EACA,QC7FU;ED8FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBChFY;EDiFZ,OCpFU;EDqFV;;AAGJ;EACI,kBCvFO;EDwFP,OCzFI;ED0FJ;;AACA;EACI,kBC1FQ;;AD8FhB;EACI,kBC7FW;ED8FX;;AAEA;EACI,kBCjGO;EDkGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCrGa;EDsGb,kBCrGmB;;ADuGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBCnImB;EDoInB,OCzIQ;ED0IR,QC/JU;EDgKV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EC9JN;;AACA;EDyJE;ICxJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAKA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA,cCtRO;EDuRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OClTP;;ADyTL;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC5UV;;AACA;EDuUM;ICtUJ;IACA;;;AD8UQ;EACI;EACA;ECnVd;;AACA;EDgVU;IC/UR;IACA;;;ADqVI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC1VN;;AD+VE;EACI,OCjWL;;ADsWP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCpYI;;ADuYR;EACI,MCzYU;;;AD8Yd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OCjbO;;;ADobX;EACI;;;AAGJ;EACI;;;AE9dJ;EACI,kBD2CmB;EC1CnB,ODsBc;ECpBV;;AAGJ;EACI;;AAGJ;EACI,ODYU;;ACTd;EACI,kBD4Be;EC3Bf,ODSO;ECRP;;AAEA;EACI,kBD0Ba;ECzBb;EACA,ODyBgB;;ACtBpB;EACI,kBDDG;ECEH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBDLI;;ACSZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,ODvBM;;ACyBN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,ODrEU;;ACyEV;EACI,MD1EM;;AC6EV;EACI,MD1DW;;ACsEnB;EACI,OD3FU;;AC+Fb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/fonts.scss","../../scss/main.scss","../../scss/variables.scss","../../scss/light.scss"],"names":[],"mappings":"AACA;EACI;EACA;;AAIJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;AAA6D;EAC7D;;AC5EJ;EACI;EACA,aCHW;EDIX;EACA;;;AAGJ;EACI;;AAEA;EACI;;;AAIR;EACI;EACA;EACA;EACA,kBCIc;EDHd,OCIQ;EDHR;EACA;EACA;EAEI;;;AAIR;EACI,OCNQ;EDOR,aC/BS;EDgCT;EACA,WC7BS;ED8BT;;;AAGJ;EACI,OCdQ;EDeR,aCvCS;EDwCT;EACA,WCpCU;;;ADuCd;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AAIR;EACI;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI,OClDW;EDmDX;EACA;;AAEA;EACI,OCtDY;;ADyDhB;EACI;;;AAIR;EACI,kBClEc;EDmEd,OCjEW;EDkEX;EACA;EACA;EACA;EACA,QC5FU;ED6FV;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,kBC/EY;EDgFZ,OCnFU;EDoFV;;AAGJ;EACI,kBCtFO;EDuFP,OCxFI;EDyFJ;;AACA;EACI,kBCzFQ;;AD6FhB;EACI,kBC5FW;ED6FX;;AAEA;EACI,kBChGO;EDiGP;;AAIR;EACI;EACA;EACA;EACA;EACA,OCpGa;EDqGb,kBCpGmB;;ADsGnB;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;;AAOZ;EACI,kBClImB;EDmInB,OCxIQ;EDyIR,QC9JU;ED+JV;EACA;EACA;;;AAIA;EACI;EACA;EACA;EACA;EACA;EC9JN;;AACA;EDwJE;ICvJA;IACA;;;AD+JA;EClKF;;AACA;EDiKE;IChKA;IACA;;;;ADsKA;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI,WC5LE;ED6LF;;AAGJ;EACI;EACA;EACA;EACA,OC/KC;;;ADqLT;EACI;EACA;;AAGJ;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAIR;EACI;;AAEA;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,OC3NA;;AD+NR;EACI,OC7NK;ED8NL;EACA;EACA;;AAEA;EACI;EACA;;AAIR;EACI;;AAEA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA,OC1PI;ED2PJ;EACA;EACA;EACA;;AAEA;EACI;EACA,kBC7PW;;ADgQf;EACI;;AAIR;EACI;;AAMA;EACI;EACA;EACA;EACA;EACA,cClRO;EDmRP;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAKR;EACI;;AAEA;EACI;;AAEA;EACI;;AAEJ;EACI,OC9SP;;ADmTL;EACI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI,kBCjUE;;ADqUF;EACI;EACA;;AAOZ;EACI;;AAEJ;EACI;EACA;EACA;EACA;EC7VV;;AACA;EDwVM;ICvVJ;IACA;;;AD+VQ;EACI;EACA;ECpWd;;AACA;EDiWU;IChWR;IACA;;;ADsWI;EACI;EACA;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,OC3WN;;ADgXE;EACI,OClXL;;ADuXP;EACI;;AACA;EACI;EACA;;;AAKZ;EACI;EACA;;;AAGJ;EACI;;AAEA;EACI,MCrZI;;ADwZR;EACI,MC1ZU;;;AD+Zd;EACI;EACA;;;AAIR;EAII;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;AAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;AACA;EACA;AAEA;EACA;AAEA;EACA;;;AAGJ;EACI;EACA;EACA;;;AAGJ;EACI,OClcO;;;ADqcX;EACI;;;AAGJ;EACI;;;AE/eJ;EACI,kBD2CmB;EC1CnB,ODsBc;ECpBV;;AAGJ;EACI;;AAGJ;EACI,ODYU;;ACTd;EACI,kBD4Be;EC3Bf,ODSO;ECRP;;AAEA;EACI,kBD0Ba;ECzBb;EACA,ODyBgB;;ACtBpB;EACI,kBDDG;ECEH,ODoBgB;ECnBhB;EACA;;AACA;EACI,kBDLI;;ACSZ;EACI,kBDRO;ECSP;;AAEA;EACI,kBDZG;ECaH;;AAIR;EACI,ODvBM;;ACyBN;EACI;EACA,kBDHY;;ACQhB;EDxCV;;AACA;ECuCU;IDtCR;IACA;;;ACyCQ;EACI,kBDbY;;ACeZ;ED/Cd;;AACA;EC8Cc;ID7CZ;IACA;;;ACmDQ;EDtDV;;AACA;ECqDU;IDpDR;IACA;;;ACwDY;ED3Dd;;AACA;EC0Dc;IDzDZ;IACA;;;AC8DI;EACI,OD7Bc;EC8Bd,kBD7BoB;;AC+BpB;EACI;;AAKZ;EACI,kBD5CoB;EC6CpB,ODrEU;;ACyEV;EACI,MD1EM;;AC6EV;EACI,MD1DW;;ACsEnB;EACI,OD3FU;;AC+Fb;EACI,ODhEM;;ACoEN;EACI,ODtEG;;;AC8EZ;EDrHF;;AACA;ECoHE;IDnHA;IACA;;;ACsHA;EDzHF;;AACA;ECwHE;IDvHA;IACA;;;;AC2HJ;EACI;;;AAGJ;EACI,OD5FY","file":"light.css"} \ No newline at end of file diff --git a/internal/ui/login/static/templates/login.html b/internal/ui/login/static/templates/login.html index 0df0371c3c..347770c129 100644 --- a/internal/ui/login/static/templates/login.html +++ b/internal/ui/login/static/templates/login.html @@ -4,10 +4,10 @@
{{if .Linking}}

{{t "Login.TitleLinking"}}

-

{{t "Login.DescriptionLinking"}}

+

{{t "Login.DescriptionLinking"}}{{if .OrgName}} {{t "Login.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{else}}

{{t "Login.Title"}}

-

{{t "Login.Description"}}

+

{{t "Login.Description"}}{{if .OrgName}} {{t "Login.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{end}}
diff --git a/internal/ui/login/static/templates/mfa_init_verify.html b/internal/ui/login/static/templates/mfa_init_verify.html index de9ff51e14..98a51e787c 100644 --- a/internal/ui/login/static/templates/mfa_init_verify.html +++ b/internal/ui/login/static/templates/mfa_init_verify.html @@ -35,6 +35,8 @@ {{end}} + {{ template "error-message" .}} +
diff --git a/internal/ui/login/static/templates/passwordless.html b/internal/ui/login/static/templates/passwordless.html index 120889be6d..70276780e2 100644 --- a/internal/ui/login/static/templates/passwordless.html +++ b/internal/ui/login/static/templates/passwordless.html @@ -26,6 +26,9 @@ {{ template "error-message" .}}
+ {{if .PasswordLogin}} + + {{end}} diff --git a/internal/ui/login/static/templates/register_org.html b/internal/ui/login/static/templates/register_org.html index fc0aeea156..ed8037bea1 100644 --- a/internal/ui/login/static/templates/register_org.html +++ b/internal/ui/login/static/templates/register_org.html @@ -15,7 +15,7 @@
- +
{{if .UserLoginMustBeDomain}} diff --git a/internal/ui/login/static/templates/select_user.html b/internal/ui/login/static/templates/select_user.html index 26f7c02ec1..fc09fa66b6 100644 --- a/internal/ui/login/static/templates/select_user.html +++ b/internal/ui/login/static/templates/select_user.html @@ -3,10 +3,10 @@
{{if .Linking}}

{{t "UserSelection.TitleLinking"}}

-

{{t "UserSelection.DescriptionLinking"}}

+

{{t "UserSelection.DescriptionLinking"}}{{if .OrgName}} {{t "UserSelection.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{else}}

{{t "UserSelection.Title"}}

-

{{t "UserSelection.Description"}}

+

{{t "UserSelection.Description"}}{{if .OrgName}} {{t "UserSelection.MustBeMemberOfOrg" "OrgName" .OrgName}}{{end}}

{{end}}
@@ -22,7 +22,7 @@
{{ range $user := .Users }} {{ $sessionState := (printf "sessionstate-%v" $user.UserSessionState) }} -
{{ 'USER.MFA.TABLETYPE' | translate }} {{'USER.MFA.TYPE.'+ mfa.type | translate}} {{ 'USER.MFA.ATTRIBUTE' | translate }} + {{ mfa.attribute }} + + {{ 'USER.MFA.TABLESTATE' | translate }} @@ -21,7 +29,7 @@ {{ 'USER.MFA.TABLEACTIONS' | translate }}