From 027a6386c0ab73dfa8502a4f2196dc1a772a0261 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Thu, 18 Feb 2021 13:41:55 +0100 Subject: [PATCH] fix: merge master (#1306) * chore(site): dependabot deps (#1148) * chore(deps): bump highlight.js from 10.4.1 to 10.5.0 in /site (#1143) Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 10.4.1 to 10.5.0. - [Release notes](https://github.com/highlightjs/highlight.js/releases) - [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md) - [Commits](https://github.com/highlightjs/highlight.js/compare/10.4.1...10.5.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @babel/plugin-transform-runtime in /site (#1144) Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.12.1 to 7.12.10. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.12.10/packages/babel-plugin-transform-runtime) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump sirv from 1.0.7 to 1.0.10 in /site (#1145) Bumps [sirv](https://github.com/lukeed/sirv) from 1.0.7 to 1.0.10. - [Release notes](https://github.com/lukeed/sirv/releases) - [Commits](https://github.com/lukeed/sirv/compare/v1.0.7...v1.0.10) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump rollup from 2.34.0 to 2.35.1 in /site (#1142) Bumps [rollup](https://github.com/rollup/rollup) from 2.34.0 to 2.35.1. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.34.0...v2.35.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @rollup/plugin-node-resolve in /site (#1141) Bumps [@rollup/plugin-node-resolve](https://github.com/rollup/plugins) from 10.0.0 to 11.0.1. - [Release notes](https://github.com/rollup/plugins/releases) - [Commits](https://github.com/rollup/plugins/compare/node-resolve-v10.0.0...commonjs-v11.0.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump marked from 1.2.5 to 1.2.7 in /site (#1140) Bumps [marked](https://github.com/markedjs/marked) from 1.2.5 to 1.2.7. - [Release notes](https://github.com/markedjs/marked/releases) - [Changelog](https://github.com/markedjs/marked/blob/master/release.config.js) - [Commits](https://github.com/markedjs/marked/compare/v1.2.5...v1.2.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @babel/core from 7.12.9 to 7.12.10 in /site (#1139) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.12.9 to 7.12.10. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.12.10/packages/babel-core) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump rollup-plugin-svelte from 6.1.1 to 7.0.0 in /site (#1138) Bumps [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) from 6.1.1 to 7.0.0. - [Release notes](https://github.com/sveltejs/rollup-plugin-svelte/releases) - [Changelog](https://github.com/sveltejs/rollup-plugin-svelte/blob/master/CHANGELOG.md) - [Commits](https://github.com/sveltejs/rollup-plugin-svelte/compare/v6.1.1...v7.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @babel/preset-env from 7.12.1 to 7.12.11 in /site (#1137) Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.1 to 7.12.11. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.12.11/packages/babel-preset-env) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * downgrade svelte plugin Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(console): dependabot deps (#1147) * chore(deps-dev): bump @types/node from 14.14.13 to 14.14.19 in /console (#1146) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.13 to 14.14.19. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump ts-protoc-gen from 0.13.0 to 0.14.0 in /console (#1129) Bumps [ts-protoc-gen](https://github.com/improbable-eng/ts-protoc-gen) from 0.13.0 to 0.14.0. - [Release notes](https://github.com/improbable-eng/ts-protoc-gen/releases) - [Changelog](https://github.com/improbable-eng/ts-protoc-gen/blob/master/CHANGELOG.md) - [Commits](https://github.com/improbable-eng/ts-protoc-gen/compare/0.13.0...0.14.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/language-service in /console (#1128) Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.0.4 to 11.0.5. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/11.0.5/packages/language-service) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/cli from 11.0.4 to 11.0.5 in /console (#1127) Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.0.4 to 11.0.5. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/compare/v11.0.4...v11.0.5) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular-devkit/build-angular in /console (#1126) Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.1100.4 to 0.1100.5. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Peintner * audit Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: e-mail templates (#1158) * View definition added * Get templates and texts from the database. * Fill in texts in templates * Fill in texts in templates * Client API added * Weekly backup * Weekly backup * Daily backup * Weekly backup * Tests added * Corrections from merge branch * Fixes from pull request review * chore(console): dependencies (#1189) * chore(deps-dev): bump @angular/language-service in /console (#1187) Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.0.5 to 11.0.9. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/11.0.9/packages/language-service) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump google-proto-files from 2.3.0 to 2.4.0 in /console (#1186) Bumps [google-proto-files](https://github.com/googleapis/nodejs-proto-files) from 2.3.0 to 2.4.0. - [Release notes](https://github.com/googleapis/nodejs-proto-files/releases) - [Changelog](https://github.com/googleapis/nodejs-proto-files/blob/master/CHANGELOG.md) - [Commits](https://github.com/googleapis/nodejs-proto-files/compare/v2.3.0...v2.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/node from 14.14.19 to 14.14.21 in /console (#1185) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.19 to 14.14.21. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/cli from 11.0.5 to 11.0.7 in /console (#1184) Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.0.5 to 11.0.7. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/compare/v11.0.5...v11.0.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump karma from 5.2.3 to 6.0.0 in /console (#1183) Bumps [karma](https://github.com/karma-runner/karma) from 5.2.3 to 6.0.0. - [Release notes](https://github.com/karma-runner/karma/releases) - [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md) - [Commits](https://github.com/karma-runner/karma/compare/v5.2.3...v6.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular-devkit/build-angular in /console (#1182) Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.1100.5 to 0.1100.7. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(console): trigger unauthenticated dialog only once (#1170) * fix: trigger dialog once * remove log * typed trigger * chore(console): dependencies (#1205) * chore(deps-dev): bump stylelint from 13.8.0 to 13.9.0 in /console (#1204) Bumps [stylelint](https://github.com/stylelint/stylelint) from 13.8.0 to 13.9.0. - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/master/CHANGELOG.md) - [Commits](https://github.com/stylelint/stylelint/compare/13.8.0...13.9.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/language-service in /console (#1203) Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.0.9 to 11.1.0. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/11.1.0/packages/language-service) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump karma from 6.0.0 to 6.0.1 in /console (#1202) Bumps [karma](https://github.com/karma-runner/karma) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/karma-runner/karma/releases) - [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md) - [Commits](https://github.com/karma-runner/karma/compare/v6.0.0...v6.0.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/cli from 11.0.7 to 11.1.1 in /console (#1201) Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.0.7 to 11.1.1. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/compare/v11.0.7...v11.1.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/jasmine from 3.6.2 to 3.6.3 in /console (#1200) Bumps [@types/jasmine](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jasmine) from 3.6.2 to 3.6.3. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jasmine) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Peintner * chore(deps-dev): bump @types/node from 14.14.21 to 14.14.22 in /console (#1199) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.21 to 14.14.22. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular-devkit/build-angular in /console (#1198) Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.1100.7 to 0.1101.1. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Peintner * chore(deps): bump angularx-qrcode from 10.0.11 to 11.0.0 in /console (#1197) Bumps [angularx-qrcode](https://github.com/cordobo/angularx-qrcode) from 10.0.11 to 11.0.0. - [Release notes](https://github.com/cordobo/angularx-qrcode/releases) - [Commits](https://github.com/cordobo/angularx-qrcode/compare/10.0.11...11.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix pack lock Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: handle sequence correctly in subscription (#1209) * fix: correct master after merges again (#1230) * chore(docs): correct `iss` claim of jwt profile (#1229) * core(docs): correct `iss` claim of jwt profile * fix: correct master after merges again (#1230) * feat(login): new palette based styles (#1149) * chore(deps-dev): bump rollup from 2.33.2 to 2.34.0 in /site (#1040) Bumps [rollup](https://github.com/rollup/rollup) from 2.33.2 to 2.34.0. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.33.2...v2.34.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump svelte-i18n from 3.2.5 to 3.3.0 in /site (#1039) Bumps [svelte-i18n](https://github.com/kaisermann/svelte-i18n) from 3.2.5 to 3.3.0. - [Release notes](https://github.com/kaisermann/svelte-i18n/releases) - [Changelog](https://github.com/kaisermann/svelte-i18n/blob/main/CHANGELOG.md) - [Commits](https://github.com/kaisermann/svelte-i18n/compare/v3.2.5...v3.3.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @rollup/plugin-url from 5.0.1 to 6.0.0 in /site (#1038) Bumps [@rollup/plugin-url](https://github.com/rollup/plugins) from 5.0.1 to 6.0.0. - [Release notes](https://github.com/rollup/plugins/releases) - [Commits](https://github.com/rollup/plugins/compare/url-v5.0.1...url-v6.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump svelte from 3.29.7 to 3.30.1 in /site (#1037) Bumps [svelte](https://github.com/sveltejs/svelte) from 3.29.7 to 3.30.1. - [Release notes](https://github.com/sveltejs/svelte/releases) - [Changelog](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md) - [Commits](https://github.com/sveltejs/svelte/compare/v3.29.7...v3.30.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump marked from 1.2.4 to 1.2.5 in /site (#1036) Bumps [marked](https://github.com/markedjs/marked) from 1.2.4 to 1.2.5. - [Release notes](https://github.com/markedjs/marked/releases) - [Changelog](https://github.com/markedjs/marked/blob/master/release.config.js) - [Commits](https://github.com/markedjs/marked/compare/v1.2.4...v1.2.5) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @babel/core from 7.12.3 to 7.12.9 in /site (#1035) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.12.3 to 7.12.9. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.12.9/packages/babel-core) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump rollup-plugin-svelte from 6.1.1 to 7.0.0 in /site (#1034) Bumps [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte) from 6.1.1 to 7.0.0. - [Release notes](https://github.com/sveltejs/rollup-plugin-svelte/releases) - [Changelog](https://github.com/sveltejs/rollup-plugin-svelte/blob/master/CHANGELOG.md) - [Commits](https://github.com/sveltejs/rollup-plugin-svelte/compare/v6.1.1...v7.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @rollup/plugin-commonjs in /site (#1033) Bumps [@rollup/plugin-commonjs](https://github.com/rollup/plugins) from 15.1.0 to 17.0.0. - [Release notes](https://github.com/rollup/plugins/releases) - [Commits](https://github.com/rollup/plugins/compare/commonjs-v15.1.0...commonjs-v17.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @rollup/plugin-node-resolve in /site (#1032) Bumps [@rollup/plugin-node-resolve](https://github.com/rollup/plugins) from 10.0.0 to 11.0.0. - [Release notes](https://github.com/rollup/plugins/releases) - [Commits](https://github.com/rollup/plugins/compare/node-resolve-v10.0.0...commonjs-v11.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @babel/preset-env from 7.12.1 to 7.12.7 in /site (#1031) Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.1 to 7.12.7. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.12.7/packages/babel-preset-env) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * go * bundle files, lgn-color, legacy theme * remove old references * light dark context, button styles, zitadel brand * button theme, edit templates * typography theme mixins * input styles, container, extend light dark palette * footer, palette, container * container, label, assets, header * action container, input, typography label, adapt button theme * a and footer styles, adapt palette * user log profile, resourcetempurl * postinstall againnn * wrochage * rm local grpc * button elevation, helper for components * radio * radio button mixins, bundle * qr code styles, secret clipboard, icon pack * stroked buttons, icon buttons, header action, typography * fix password policy styles * account selection * account selection, lgn avatar * mocks * template fixes, animations scss * checkbox, register temp * checkbox appr * fix checkbox, remove input interference * select theme * avatar script, user selection, password policy validation fix * fix formfield state for register and change pwd * footer, main style, qr code fix, mfa type fix, account sel, checkbox * fotter tos, user select * reverse buttons for intial submit action * theme script, themed error messages, header img source * content wrapper, i18n, mobile * emptyline * idp mixins, fix unstyled html * register container * register layout, list themes, policy theme, register org * massive asset cleanup * fix source path, add missing icon, fix complexity refs, prefix * remove material icons, unused assets, fix icon font * move icon pack * avatar, contrast theme, error fix * zitadel css map * revert go mod * fix mfa verify actions * add idp styles * fix google colors, idp styles * fix: bugs * fix register options, google * fix script, mobile layout * precompile font selection * go mod tidy * assets and cleanup * input suffix, fix alignment, actions, add progress bar themes * progress bar mixins, layout fixes * remove test from loginname * cleanup comments, scripts * clear comments * fix external back button * fix mfa alignment * fix actions layout, on dom change listener for suffix * free tier change, success label * fix: button font line-height * remove tabindex * remove comment * remove comment * Update internal/ui/login/handler/password_handler.go Co-authored-by: Livio Amstutz Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Maximilian Peintner Co-authored-by: Livio Amstutz * chore(console): dependencies (#1233) * chore(deps-dev): bump @angular-devkit/build-angular in /console (#1214) Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.1101.1 to 0.1101.2. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump karma from 6.0.1 to 6.0.3 in /console (#1215) Bumps [karma](https://github.com/karma-runner/karma) from 6.0.1 to 6.0.3. - [Release notes](https://github.com/karma-runner/karma/releases) - [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md) - [Commits](https://github.com/karma-runner/karma/compare/v6.0.1...v6.0.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/language-service in /console (#1216) Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.1.0 to 11.1.1. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/11.1.1/packages/language-service) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/cli from 11.1.1 to 11.1.2 in /console (#1217) Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.1.1 to 11.1.2. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/compare/v11.1.1...v11.1.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Peintner * lock * site deps Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: get email texts with default language (#1238) * fix(login): mail verification (#1237) * fix: mail verification * not block, stroked * fix: issues of new login ui (#1241) * fix: i18n of register * fix: autofocus * feat(operator): zitadel and database operator (#1208) * feat(operator): add base for zitadel operator * fix(operator): changed pipeline to release operator * fix(operator): fmt with only one parameter * fix(operator): corrected workflow job name * fix(zitadelctl): added restore and backuplist command * fix(zitadelctl): scale for restore * chore(container): use scratch for deploy container * fix(zitadelctl): limit image to scratch * fix(migration): added migration scripts for newer version * fix(operator): changed handling of kubeconfig in operator logic * fix(operator): changed handling of secrets in operator logic * fix(operator): use new version of zitadel * fix(operator): added path for migrations * fix(operator): delete doublets of migration scripts * fix(operator): delete subpaths and integrate logic into init container * fix(operator): corrected path in dockerfile for local migrations * fix(operator): added migrations for cockroachdb-secure * fix(operator): delete logic for ambassador module * fix(operator): added read and write secret commands * fix(operator): correct and align operator pipeline with zitadel pipeline * fix(operator): correct yaml error in operator pipeline * fix(operator): correct action name in operator pipeline * fix(operator): correct case-sensitive filename in operator pipeline * fix(operator): upload artifacts from buildx output * fix(operator): corrected attribute spelling error * fix(operator): combined jobs for operator binary and image * fix(operator): added missing comma in operator pipeline * fix(operator): added codecov for operator image * fix(operator): added codecov for operator image * fix(testing): code changes for testing and several unit-tests (#1009) * fix(operator): usage of interface of kubernetes client for testing and several unit-tests * fix(operator): several unit-tests * fix(operator): several unit-tests * fix(operator): changed order for the operator logic * fix(operator): added version of zitadelctl from semantic release * fix(operator): corrected function call with version of zitadelctl * fix(operator): corrected function call with version of zitadelctl * fix(operator): add check output to operator release pipeline * fix(operator): set --short length everywhere to 12 * fix(operator): zitadel setup in job instead of exec with several unit tests * fix(operator): fixes to combine newest zitadel and testing branch * fix(operator): corrected path in Dockerfile * fix(operator): fixed unit-test that was ignored during changes * fix(operator): fixed unit-test that was ignored during changes * fix(operator): corrected Dockerfile to correctly use env variable * fix(operator): quickfix takeoff deployment * fix(operator): corrected the clusterrolename in the applied artifacts * fix: update secure migrations * fix(operator): migrations (#1057) * fix(operator): copied migrations from orbos repository * fix(operator): newest migrations * chore: use cockroach-secure * fix: rename migration * fix: remove insecure cockroach migrations Co-authored-by: Stefan Benz * fix: finalize labels * fix(operator): cli logging concurrent and fixe deployment of operator during restore * fix: finalize labels and cli commands * fix: restore * chore: cockroachdb is always secure * chore: use orbos consistent-labels latest commit * test: make tests compatible with new labels * fix: default to sa token for start command * fix: use cockroachdb v12.02 * fix: don't delete flyway user * test: fix migration test * fix: use correct table qualifiers * fix: don't alter sequence ownership * fix: upgrade flyway * fix: change ownership of all dbs and tables to admin user * fix: change defaultdb user * fix: treat clientid status codes >= 400 as errors * fix: reconcile specified ZITADEL version, not binary version * fix: add ca-certs * fix: use latest orbos code * fix: use orbos with fixed race condition * fix: use latest ORBOS code * fix: use latest ORBOS code * fix: make migration and scaling around restoring work * fix(operator): move zitadel operator * chore(migrations): include owner change migration * feat(db): add code base for database operator * fix(db): change used image registry for database operator * fix(db): generated mock * fix(db): add accidentally ignored file * fix(db): add cockroachdb backup image to pipeline * fix(db): correct pipeline and image versions * fix(db): correct version of used orbos * fix(db): correct database import * fix(db): go mod tidy * fix(db): use new version for orbos * fix(migrations): include migrations into zitadelctl binary (#1211) * fix(db): use statik to integrate migrations into binary * fix(migrations): corrections unit tests and pipeline for integrated migrations into zitadelctl binary * fix(migrations): correction in dockerfile for pipeline build * fix(migrations): correction in dockerfile for pipeline build * fix(migrations): dockerfile changes for cache optimization * fix(database): correct used part-of label in database operator * fix(database): correct used selectable label in zitadel operator * fix(operator): correct lables for user secrets in zitadel operator * fix(operator): correct lables for service test in zitadel operator * fix: don't enable database features for user operations (#1227) * fix: don't enable database features for user operations * fix: omit database feature for connection info adapter * fix: use latest orbos version * fix: update ORBOS (#1240) Co-authored-by: Florian Forster Co-authored-by: Elio Bischof * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * fix: usermemberships in authz (#1288) * fix: usermemberships in authz * fix: tests * fix: migration * fix: handler * fix: my usermemberships (#1290) * fix: my usermemberships * frontend Co-authored-by: Max Peintner * fix: my usermemberships (#1291) * fix: my usermemberships * fix: migration * fix: migration (#1293) * fix(login): chrome prefill, org register suffix offset, loginname overflow (#1292) * fix: calculate offset, fix prefill * fix loginname, displayname overflow * feat: docs rehaul, fix missing context in console, quickstarts (#1212) * onboarding components, routing, steps * onboarding component, toc * fix onboarding mixin * header * refactor docs * fix layout * cleanup routing * docs routing * fix conventions * de en routing * docs, guide contents, nav * rem i18n support * fix routing from docs * rollup onwarn changes, preload * update svelte plugin, update rollup config * move docs * revert img style, remove code table * rem de completely * rollup optim, template * angular quickstart, quickstart overview page, update deps * fix link * pack, slug * prefetch binding, hidden links * export log * guards route ch * fix homepage * angular docs * docs * resolve fsh * overview * docs * docs * packages fix race condition * nav, home link * add vue, aspnet * doc optimizations * embed status pal * angular guide * angular guide * dotnet, angular guide * viewbox * typo * block onboarding route for non iam writers * set links from component data * fix: fetch org context in guard, more main cnt (#1192) * change get started guide, fix code blockquotes, typos * flutter guide * h2 spacing * highlight strong * plus * rm start sublinks * add proxy quickstart * regex * prevent outside click, fix project grant write Co-authored-by: Florian Forster Co-authored-by: Livio Amstutz * fix(console): auth guard, i18n (#1296) * fix: auth guard, i18n * Update console/src/app/guards/auth.guard.ts Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * feat(console): OIDC setup (#1272) * feat: delete app * radio button mods, i18n * radio style, recommended flag * fix form, emitter, module, styles * app oidc * form value change * cleanup * app grid, new app detail, redirect, i18n * new uri format * seperate uris * cleanup export, create redirect * fix custom two way binding, switch * chore(deps): bump grpc from 1.24.3 to 1.24.5 in /console (#1287) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps): bump grpc from 1.24.3 to 1.24.5 in /console Bumps [grpc](https://github.com/grpc/grpc-node) from 1.24.3 to 1.24.5. - [Release notes](https://github.com/grpc/grpc-node/releases) - [Commits](https://github.com/grpc/grpc-node/compare/grpc@1.24.3...grpc@1.24.5) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/node from 14.14.22 to 14.14.28 in /console (#1286) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump @types/node from 14.14.22 to 14.14.28 in /console Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.22 to 14.14.28. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular-devkit/build-angular from 0.1101.2 to 0.1102.0 in /console (#1285) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump @angular-devkit/build-angular in /console Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.1101.2 to 0.1102.0. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/commits) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump typescript from 4.0.5 to 4.0.7 in /console (#1284) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump typescript from 4.0.5 to 4.0.7 in /console Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.0.5 to 4.0.7. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.0.5...v4.0.7) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump karma from 6.0.3 to 6.1.1 in /console (#1283) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump karma from 6.0.3 to 6.1.1 in /console Bumps [karma](https://github.com/karma-runner/karma) from 6.0.3 to 6.1.1. - [Release notes](https://github.com/karma-runner/karma/releases) - [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md) - [Commits](https://github.com/karma-runner/karma/compare/v6.0.3...v6.1.1) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/language-service from 11.1.1 to 11.2.0 in /console (#1282) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump @angular/language-service in /console Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.1.1 to 11.2.0. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/11.2.0/packages/language-service) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump stylelint from 13.9.0 to 13.10.0 in /console (#1281) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump stylelint from 13.9.0 to 13.10.0 in /console Bumps [stylelint](https://github.com/stylelint/stylelint) from 13.9.0 to 13.10.0. - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/master/CHANGELOG.md) - [Commits](https://github.com/stylelint/stylelint/compare/13.9.0...13.10.0) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @angular/cli from 11.1.2 to 11.2.0 in /console (#1280) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump @angular/cli from 11.1.2 to 11.2.0 in /console Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.1.2 to 11.2.0. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/compare/v11.1.2...v11.2.0) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump stylelint-scss from 3.18.0 to 3.19.0 in /console (#1279) * chore: add local migrate_local.go again (#1261) * chore: pass params in migrate_local.go (#1264) * fix: login policy bug (#1268) * fix: permissions on login policy multifactors and secondfactors * fix idp restriction Co-authored-by: Max Peintner * fix: redirect after idp create (#1269) * fix(pipeline): corrected and combined operator and zitadel release into combined workflow (#1273) * fix(pipeline): combined operator and zitadel workflow to only release once * fix(pipeline): add dev releases for zitadelctl * fix(pipeline): delete unused name attribute * fix(pipeline): corrected use of github token env-variable * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected download of artifacts to globally defined folder * fix(pipeline): corrected ref to get branch name for release * fix(pipeline): last corrections and use of different github action (#1270) * fix(pipeline): corrected loop for dev release * fix(pipeline): exclude tags from starting build workflow * fix(pipeline): use different release create action for already existing release * fix(pipeline): use correct name for release * fix(pipeline): push image with branch name tag and replace slashes with underscores * fix(pipeline): corrected indenting for yaml syntax * fix(pipeline): corrected handling of branch name * fix(pipeline): list artifacts after download * fix(pipeline): use github env for artifacts folder * fix(pipeline): replace slash with underscore in all jobs * fix(pipeline): pre-calculate refs for all jobs * fix(pipeline): corrected yaml indenting * fix(pipeline): deleted missed step * fix(pipeline): deleted unexpected input for dev-release * fix(pipeline): corrected echo for version in refs job * fix(pipeline): remove empty if in job * chore(pipeline): use correct path to zitadelctl binaries (#1277) * fix(pipeline): use correct version for zitadelctl build (#1278) * chore(deps-dev): bump stylelint-scss from 3.18.0 to 3.19.0 in /console Bumps [stylelint-scss](https://github.com/kristerkari/stylelint-scss) from 3.18.0 to 3.19.0. - [Release notes](https://github.com/kristerkari/stylelint-scss/releases) - [Changelog](https://github.com/kristerkari/stylelint-scss/blob/master/CHANGELOG.md) - [Commits](https://github.com/kristerkari/stylelint-scss/compare/3.18.0...3.19.0) Signed-off-by: dependabot[bot] Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Max Peintner Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix custom change, highlight current config, links * info app-detail * app card component * applications list, fix project-grant-owner * fix member write * colorize warn in app * redirect warnings * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz * Update console/src/assets/i18n/en.json Co-authored-by: Livio Amstutz * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz * remove comments * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz * Update console/src/assets/i18n/de.json Co-authored-by: Livio Amstutz Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Livio Amstutz Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> * fix: primary button color (#1297) * fix: remove status, admin line width (#1298) * feat: token introspection, api clients and auth method private_key_jwt (#1276) * introspect * testingapplication key * date * client keys * fix client keys * fix client keys * access tokens only for users * AuthMethodPrivateKeyJWT * client keys * set introspection info correctly * managae apis * update oidc pkg * cleanup * merge msater * set current sequence in migration * set current sequence in migration * set current sequence in migration * Apply suggestions from code review Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * DeleteAuthNKeysByObjectID * ensure authn keys uptodate * update oidc version * merge master * merge master Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * merge master * fix: version of migration for auth keys * merge master * merge master * fix step 11 Co-authored-by: Max Peintner Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Waeger <49439088+michaelulrichwaeger@users.noreply.github.com> Co-authored-by: Maximilian Peintner Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: Florian Forster Co-authored-by: Elio Bischof --- .github/workflows/zitadel.yml | 91 - .releaserc.js | 6 +- cmd/zitadel/setup.yaml | 4 +- cmd/zitadel/startup.yaml | 5 + cmd/zitadel/system-defaults.yaml | 1 + console/package-lock.json | 3781 ++++++++++------- console/package.json | 18 +- console/src/app/app-routing.module.ts | 9 + console/src/app/app.component.html | 43 +- console/src/app/app.component.scss | 55 +- console/src/app/app.component.ts | 2 +- console/src/app/app.module.ts | 2 + console/src/app/guards/auth.guard.ts | 5 +- .../modules/app-card/app-card.component.html | 5 + .../modules/app-card/app-card.component.scss | 49 + .../modules/app-card/app-card.component.ts | 14 + .../app/modules/app-card/app-card.module.ts | 15 + .../app-auth-method-radio.component.html | 35 + .../app-auth-method-radio.component.scss | 140 + .../app-auth-method-radio.component.ts | 32 + .../app/modules/app-radio/app-radio.module.ts | 27 + .../app-type-radio.component.html | 14 + .../app-type-radio.component.scss | 68 + .../app-type-radio.component.ts | 27 + .../src/app/modules/card/card.component.html | 3 +- .../src/app/modules/card/card.component.scss | 2 + .../src/app/modules/card/card.component.ts | 2 +- .../idp-create/idp-create.component.ts | 6 +- .../idp-table/idp-table.component.html | 4 +- .../info-section/info-section.component.html | 6 +- .../info-section/info-section.component.scss | 30 +- .../info-section/info-section.component.ts | 12 +- .../onboarding/onboarding-routing.module.ts | 18 + .../onboarding/onboarding.component.html | 27 + .../onboarding/onboarding.component.scss | 147 + .../onboarding/onboarding.component.spec.ts | 25 + .../onboarding/onboarding.component.ts | 16 + .../modules/onboarding/onboarding.module.ts | 18 + .../login-policy/login-policy.component.html | 4 +- .../src/app/pages/home/home.component.html | 98 +- .../src/app/pages/home/home.component.scss | 73 + console/src/app/pages/home/home.component.ts | 18 + console/src/app/pages/home/home.module.ts | 8 +- .../apps/app-create/app-create.component.html | 157 +- .../apps/app-create/app-create.component.scss | 54 +- .../apps/app-create/app-create.component.ts | 183 +- .../apps/app-detail/app-detail.component.html | 258 +- .../apps/app-detail/app-detail.component.scss | 117 +- .../apps/app-detail/app-detail.component.ts | 156 +- .../app/pages/projects/apps/apps.module.ts | 6 + .../app/pages/projects/apps/authmethods.ts | 166 + .../src/app/pages/projects/apps/authtypes.ts | 25 + .../redirect-uris.component.html | 28 + .../redirect-uris.component.scss | 61 + .../redirect-uris.component.spec.ts | 25 + .../redirect-uris/redirect-uris.component.ts | 43 + .../application-grid.component.html | 19 +- .../application-grid.component.scss | 106 +- .../application-grid.component.ts | 8 +- .../applications/applications.component.html | 15 +- .../applications/applications.component.ts | 2 +- .../owned-project-detail.component.html | 13 +- .../owned-project-detail.module.ts | 2 + .../project-grant-detail.component.html | 2 +- .../pages/signedout/signedout.component.html | 5 +- .../pages/signedout/signedout.component.scss | 7 +- .../auth-user-detail.component.html | 2 +- .../memberships/memberships.component.ts | 19 +- .../user-detail/user-detail.component.scss | 1 - console/src/app/services/grpc-auth.service.ts | 13 + .../services/interceptors/auth.interceptor.ts | 1 + console/src/app/services/mgmt.service.ts | 7 + console/src/assets/i18n/de.json | 104 +- console/src/assets/i18n/en.json | 104 +- .../assets/images/zitadel-logo-solo-dark.svg | 76 + .../assets/images/zitadel-logo-solo-light.svg | 74 + console/src/component-themes.scss | 10 +- console/src/styles.scss | 3 + console/src/styles/sidenav-list.scss | 22 +- console/src/styles/table.scss | 3 +- go.mod | 6 +- go.sum | 53 +- internal/api/authz/context.go | 21 + internal/api/authz/permissions.go | 46 +- internal/api/authz/permissions_test.go | 112 +- internal/api/authz/token.go | 7 +- internal/api/authz/token_test.go | 2 +- internal/api/grpc/auth/user.go | 10 + internal/api/grpc/auth/user_converter.go | 91 +- .../api/grpc/auth/user_human_converter.go | 2 +- .../api/grpc/auth/user_machine_converter.go | 36 +- internal/api/grpc/management/application.go | 52 + .../grpc/management/application_converter.go | 235 +- .../grpc/management/user_machine_converter.go | 25 +- .../middleware/auth_interceptor_test.go | 3 +- internal/api/oidc/client.go | 74 +- internal/api/oidc/client_converter.go | 16 +- internal/api/oidc/op.go | 18 +- .../eventsourcing/eventstore/org.go | 9 + .../eventsourcing/eventstore/token.go | 13 +- .../eventsourcing/eventstore/user.go | 8 +- .../eventsourcing/eventstore/user_grant.go | 96 +- .../eventsourcing/handler/application.go | 2 + .../eventsourcing/handler/authn_keys.go | 120 + .../eventsourcing/handler/handler.go | 2 +- .../eventsourcing/handler/machine_keys.go | 110 - .../repository/eventsourcing/handler/token.go | 4 +- .../repository/eventsourcing/repository.go | 6 +- .../eventsourcing/view/authn_keys.go | 74 + .../eventsourcing/view/machine_keys.go | 74 - internal/auth/repository/org.go | 2 + internal/auth/repository/user.go | 6 +- .../eventsourcing/eventstore/user_grant.go | 119 +- .../eventsourcing/handler/handler.go | 14 +- .../eventsourcing/handler/user_membership.go | 274 ++ .../repository/eventsourcing/repository.go | 6 +- .../eventsourcing/view/user_membership.go | 98 + .../config/systemdefaults/system_defaults.go | 1 + .../eventsourcing/eventstore_test.go | 318 ++ .../iam/repository/eventsourcing/model/iam.go | 14 +- internal/key/model/authn_key.go | 101 + .../key/repository/view/authn_key_view.go | 77 + .../key/repository/view/model/authn_key.go | 171 + .../repository/view/model/authn_key_query.go | 63 + .../eventsourcing/eventstore/permissions.go | 2 +- .../eventsourcing/eventstore/project.go | 76 + .../eventsourcing/eventstore/user.go | 21 +- .../eventsourcing/handler/application.go | 2 + .../eventsourcing/handler/authn_keys.go | 120 + .../eventsourcing/handler/handler.go | 2 +- .../eventsourcing/handler/machine_keys.go | 110 - .../eventsourcing/view/authn_keys.go | 74 + .../eventsourcing/view/machine_keys.go | 70 - internal/management/repository/project.go | 8 + internal/management/repository/user.go | 5 +- internal/project/model/api_config.go | 62 + internal/project/model/application.go | 20 +- internal/project/model/oidc_config.go | 30 +- .../repository/eventsourcing/eventstore.go | 197 +- .../eventsourcing/model/api_config.go | 87 + .../eventsourcing/model/application.go | 11 +- .../eventsourcing/model/oidc_config.go | 146 +- .../repository/eventsourcing/model/project.go | 8 + .../repository/eventsourcing/model/types.go | 7 + .../repository/eventsourcing/project.go | 77 + .../repository/view/model/application.go | 22 +- internal/static/i18n/de.yaml | 6 +- internal/static/i18n/en.yaml | 9 +- .../login/handler/external_login_handler.go | 6 +- .../handler/external_register_handler.go | 5 +- .../static/resources/scripts/form_submit.js | 2 + .../resources/scripts/loginname_suffix.js | 17 + .../account_selection/account_selection.scss | 5 + .../resources/themes/zitadel/css/bundle.css | 10 + .../themes/zitadel/css/bundle.css.map | 2 +- .../resources/themes/zitadel/css/zitadel.css | 5 + .../themes/zitadel/css/zitadel.css.map | 2 +- .../static/templates/link_users_done.html | 2 +- internal/user/model/machine_key.go | 54 - internal/user/model/user_machine.go | 10 +- .../repository/eventsourcing/eventstore.go | 8 +- .../eventsourcing/model/user_machine.go | 3 +- .../user/repository/view/machine_key_view.go | 77 - .../user/repository/view/model/machine_key.go | 84 - .../view/model/machine_key_query.go | 61 - internal/user/repository/view/model/token.go | 1 + internal/v2/command/setup_step11.go | 14 +- .../v2/command/unique_constraints_model.go | 6 +- internal/v2/domain/application_oidc.go | 1 + .../cockroach/V1.29__user_memberships.sql | 18 + .../cockroach/V1.30__user_memberships.sql | 47 + migrations/cockroach/V1.31__auth_keys.sql | 92 + ...ue_tables.sql => V1.32__unique_tables.sql} | 0 ...1.30__policies.sql => V1.33__policies.sql} | 0 pkg/grpc/auth/mock/auth.proto.mock.go | 20 + pkg/grpc/auth/proto/auth.proto | 61 +- pkg/grpc/management/proto/management.proto | 182 +- site/config.js | 2 +- site/docs/administrate/00-overview.de.md | 5 - .../{00-overview.en.md => 00-overview.md} | 2 + site/docs/administrate/01-console.de.md | 5 - .../{01-console.en.md => 01-console.md} | 0 site/docs/administrate/02-organisations.de.md | 5 - ...rganisations.en.md => 02-organisations.md} | 0 site/docs/administrate/03-projects.de.md | 5 - .../{03-projects.en.md => 03-projects.md} | 0 site/docs/administrate/04-clients.de.md | 5 - .../{04-clients.en.md => 04-clients.md} | 2 +- site/docs/administrate/05-roles.de.md | 5 - .../{05-roles.en.md => 05-roles.md} | 2 +- site/docs/administrate/06-users.de.md | 5 - .../{06-users.en.md => 06-users.md} | 0 site/docs/administrate/07-policies.de.md | 5 - .../{07-policies.en.md => 07-policies.md} | 0 site/docs/administrate/08-providers.de.md | 5 - .../{08-providers.en.md => 08-providers.md} | 0 .../docs/administrate/09-authorizations.de.md | 5 - ...horizations.en.md => 09-authorizations.md} | 0 .../administrate/09-management-roles.de.md | 5 - ...ent-roles.en.md => 09-management-roles.md} | 0 site/docs/administrate/70-zitadelroles.de.md | 5 - ...-zitadelroles.en.md => 70-zitadelroles.md} | 0 site/docs/administrate/80-audit.de.md | 5 - .../{80-audit.en.md => 80-audit.md} | 0 site/docs/administrate/90-system.de.md | 5 - .../{90-system.en.md => 90-system.md} | 2 +- site/docs/administrate/seo_de.html | 35 - site/docs/administrate/seo_en.html | 35 - site/docs/angular/00-overview.md | 16 + site/docs/angular/01-configure.md | 29 + site/docs/angular/02-code.md | 198 + site/docs/angular/03-end.md | 9 + .../00-overview.en.md => apis/00-overview.md} | 2 + .../01-authentication.md} | 0 .../02-management.md} | 0 .../03-administration.md} | 0 .../00-overview.md} | 2 + .../01-priciples.md} | 0 .../02-architecture.md} | 1 + .../03-openidoauth.md} | 10 +- site/docs/aspnet/00-overview.md | 15 + site/docs/develop/00-overview.de.md | 6 - site/docs/develop/01-authentication.de.md | 6 - site/docs/develop/02-management.de.md | 6 - site/docs/develop/03-administration.de.md | 6 - site/docs/documentation/00-overview.de.md | 5 - site/docs/documentation/01-priciples.de.md | 20 - site/docs/documentation/02-architecture.de.md | 5 - site/docs/documentation/03-openidoauth.de.md | 5 - site/docs/export-log | 78 + site/docs/flutter/00-overview.md | 14 + site/docs/go/00-overview.md | 15 + site/docs/integrate/00-overview.de.md | 6 - site/docs/integrate/01-singlepageapp.de.md | 6 - site/docs/integrate/02-serverapp.de.md | 6 - site/docs/integrate/03-mobileapp.de.md | 6 - site/docs/integrate/04-nativeapp.de.md | 6 - site/docs/integrate/05-proxy.de.md | 6 - site/docs/integrate/06-api.de.md | 6 - site/docs/integrate/07-products.de.md | 6 - site/docs/oauth2-proxy/00-overview.md | 13 + .../docs/oauth2-proxy/01-configure-zitadel.md | 29 + site/docs/oauth2-proxy/02-configure-proxy.md | 28 + site/docs/oauth2-proxy/03-end.md | 7 + .../00-overview.md} | 2 +- .../01-singlepageapp.md} | 2 +- .../02-serverapp.md} | 0 .../03-mobileapp.md} | 0 .../04-nativeapp.md} | 0 .../05-proxy.md} | 0 .../06-api.en.md => quickstarts/06-api.md} | 0 .../07-products.md} | 0 site/docs/start/00-quick-start.de.md | 6 - site/docs/start/00-quick-start.en.md | 54 - site/docs/start/00-quick-start.md | 19 + site/docs/start/01-zitadel-ch.md | 39 + site/docs/start/02-orbos.md | 7 + site/docs/start/03-static-manifest.md | 7 + site/docs/use/00-user.de.md | 5 - site/docs/use/{00-user.en.md => 00-user.md} | 2 + site/docs/vue/00-overview.md | 15 + site/package-lock.json | 258 +- site/package.json | 23 +- site/quickstarts/000-vue.md | 13 + site/quickstarts/100-angular.md | 13 + site/quickstarts/200-flutter.md | 12 + site/quickstarts/300-go.md | 12 + site/quickstarts/400-aspnet.md | 12 + site/quickstarts/500-OAuth2-Proxy.md | 11 + site/rollup.config.js | 47 +- site/src/client.js | 9 +- site/src/components/CodeTable.svelte | 121 - site/src/components/Docs.svelte | 63 +- site/src/components/DocsHeader.svelte | 172 - site/src/components/GuideContents.svelte | 3 +- site/src/components/LanguageSwitcher.svelte | 57 - site/src/components/Nav.svelte | 286 +- site/src/components/NavItem.svelte | 33 +- site/src/components/SearchSelector.svelte | 5 +- site/src/components/SearchTrigger.svelte | 3 +- site/src/i18n.js | 83 - site/src/messages/de.json | 50 - site/src/messages/en.json | 49 - site/src/modules/cookie.js | 33 - site/src/modules/language-store.js | 8 - site/src/routes/[slug].json.js | 27 +- site/src/routes/[slug].svelte | 9 +- site/src/routes/_error.svelte | 6 +- site/src/routes/_layout.svelte | 34 +- site/src/routes/index.svelte | 273 +- site/src/routes/quickstarts/_quickstarts.js | 59 + site/src/routes/quickstarts/index.json.js | 25 + site/src/routes/quickstarts/index.svelte | 154 + site/src/server.js | 3 - site/src/service-worker.js | 17 +- site/src/template.html | 2 +- site/src/utils/generate_code_tabs.js | 3 - site/src/utils/generate_docs.js | 36 +- site/src/utils/generate_seo.js | 4 +- site/src/utils/highlight.js | 16 + site/src/utils/language_extractor.js | 21 - site/src/utils/photoswipe.js | 3 +- site/src/utils/slug.js | 75 +- site/static/base.css | 19 +- site/static/img/accounts_org_register.png | Bin 73051 -> 0 bytes .../img/accounts_org_register_light.png | Bin 0 -> 131439 bytes site/static/tech/android.svg | 1 + site/static/tech/angular.svg | 16 + site/static/tech/aspnet.png | Bin 0 -> 67884 bytes site/static/tech/dart.svg | 11 + site/static/tech/flutter.svg | 1 + site/static/tech/golang.svg | 55 + site/static/tech/net.svg | 1 + site/static/tech/oauth2-proxy.svg | 1 + site/static/tech/react.png | Bin 0 -> 46955 bytes site/static/tech/vue.svg | 2 + 316 files changed, 9632 insertions(+), 4919 deletions(-) delete mode 100644 .github/workflows/zitadel.yml create mode 100644 console/src/app/modules/app-card/app-card.component.html create mode 100644 console/src/app/modules/app-card/app-card.component.scss create mode 100644 console/src/app/modules/app-card/app-card.component.ts create mode 100644 console/src/app/modules/app-card/app-card.module.ts create mode 100644 console/src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component.html create mode 100644 console/src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component.scss create mode 100644 console/src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component.ts create mode 100644 console/src/app/modules/app-radio/app-radio.module.ts create mode 100644 console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.html create mode 100644 console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.scss create mode 100644 console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.ts create mode 100644 console/src/app/modules/onboarding/onboarding-routing.module.ts create mode 100644 console/src/app/modules/onboarding/onboarding.component.html create mode 100644 console/src/app/modules/onboarding/onboarding.component.scss create mode 100644 console/src/app/modules/onboarding/onboarding.component.spec.ts create mode 100644 console/src/app/modules/onboarding/onboarding.component.ts create mode 100644 console/src/app/modules/onboarding/onboarding.module.ts create mode 100644 console/src/app/pages/projects/apps/authmethods.ts create mode 100644 console/src/app/pages/projects/apps/authtypes.ts create mode 100644 console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.html create mode 100644 console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.scss create mode 100644 console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.spec.ts create mode 100644 console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.ts create mode 100644 console/src/assets/images/zitadel-logo-solo-dark.svg create mode 100644 console/src/assets/images/zitadel-logo-solo-light.svg create mode 100644 internal/auth/repository/eventsourcing/handler/authn_keys.go delete mode 100644 internal/auth/repository/eventsourcing/handler/machine_keys.go create mode 100644 internal/auth/repository/eventsourcing/view/authn_keys.go delete mode 100644 internal/auth/repository/eventsourcing/view/machine_keys.go create mode 100644 internal/authz/repository/eventsourcing/handler/user_membership.go create mode 100644 internal/authz/repository/eventsourcing/view/user_membership.go create mode 100644 internal/key/model/authn_key.go create mode 100644 internal/key/repository/view/authn_key_view.go create mode 100644 internal/key/repository/view/model/authn_key.go create mode 100644 internal/key/repository/view/model/authn_key_query.go create mode 100644 internal/management/repository/eventsourcing/handler/authn_keys.go delete mode 100644 internal/management/repository/eventsourcing/handler/machine_keys.go create mode 100644 internal/management/repository/eventsourcing/view/authn_keys.go delete mode 100644 internal/management/repository/eventsourcing/view/machine_keys.go create mode 100644 internal/project/model/api_config.go create mode 100644 internal/project/repository/eventsourcing/model/api_config.go delete mode 100644 internal/user/model/machine_key.go delete mode 100644 internal/user/repository/view/machine_key_view.go delete mode 100644 internal/user/repository/view/model/machine_key.go delete mode 100644 internal/user/repository/view/model/machine_key_query.go create mode 100644 migrations/cockroach/V1.29__user_memberships.sql create mode 100644 migrations/cockroach/V1.30__user_memberships.sql create mode 100644 migrations/cockroach/V1.31__auth_keys.sql rename migrations/cockroach/{V1.29__unique_tables.sql => V1.32__unique_tables.sql} (100%) rename migrations/cockroach/{V1.30__policies.sql => V1.33__policies.sql} (100%) delete mode 100644 site/docs/administrate/00-overview.de.md rename site/docs/administrate/{00-overview.en.md => 00-overview.md} (88%) delete mode 100644 site/docs/administrate/01-console.de.md rename site/docs/administrate/{01-console.en.md => 01-console.md} (100%) delete mode 100644 site/docs/administrate/02-organisations.de.md rename site/docs/administrate/{02-organisations.en.md => 02-organisations.md} (100%) delete mode 100644 site/docs/administrate/03-projects.de.md rename site/docs/administrate/{03-projects.en.md => 03-projects.md} (100%) delete mode 100644 site/docs/administrate/04-clients.de.md rename site/docs/administrate/{04-clients.en.md => 04-clients.md} (98%) delete mode 100644 site/docs/administrate/05-roles.de.md rename site/docs/administrate/{05-roles.en.md => 05-roles.md} (95%) delete mode 100644 site/docs/administrate/06-users.de.md rename site/docs/administrate/{06-users.en.md => 06-users.md} (100%) delete mode 100644 site/docs/administrate/07-policies.de.md rename site/docs/administrate/{07-policies.en.md => 07-policies.md} (100%) delete mode 100644 site/docs/administrate/08-providers.de.md rename site/docs/administrate/{08-providers.en.md => 08-providers.md} (100%) delete mode 100644 site/docs/administrate/09-authorizations.de.md rename site/docs/administrate/{09-authorizations.en.md => 09-authorizations.md} (100%) delete mode 100644 site/docs/administrate/09-management-roles.de.md rename site/docs/administrate/{09-management-roles.en.md => 09-management-roles.md} (100%) delete mode 100644 site/docs/administrate/70-zitadelroles.de.md rename site/docs/administrate/{70-zitadelroles.en.md => 70-zitadelroles.md} (100%) delete mode 100644 site/docs/administrate/80-audit.de.md rename site/docs/administrate/{80-audit.en.md => 80-audit.md} (100%) delete mode 100644 site/docs/administrate/90-system.de.md rename site/docs/administrate/{90-system.en.md => 90-system.md} (98%) delete mode 100644 site/docs/administrate/seo_de.html delete mode 100644 site/docs/administrate/seo_en.html create mode 100644 site/docs/angular/00-overview.md create mode 100644 site/docs/angular/01-configure.md create mode 100644 site/docs/angular/02-code.md create mode 100644 site/docs/angular/03-end.md rename site/docs/{develop/00-overview.en.md => apis/00-overview.md} (95%) rename site/docs/{develop/01-authentication.en.md => apis/01-authentication.md} (100%) rename site/docs/{develop/02-management.en.md => apis/02-management.md} (100%) rename site/docs/{develop/03-administration.en.md => apis/03-administration.md} (100%) rename site/docs/{documentation/00-overview.en.md => architecture/00-overview.md} (83%) rename site/docs/{documentation/01-priciples.en.md => architecture/01-priciples.md} (100%) rename site/docs/{documentation/02-architecture.en.md => architecture/02-architecture.md} (99%) rename site/docs/{documentation/03-openidoauth.en.md => architecture/03-openidoauth.md} (97%) create mode 100644 site/docs/aspnet/00-overview.md delete mode 100644 site/docs/develop/00-overview.de.md delete mode 100644 site/docs/develop/01-authentication.de.md delete mode 100644 site/docs/develop/02-management.de.md delete mode 100644 site/docs/develop/03-administration.de.md delete mode 100644 site/docs/documentation/00-overview.de.md delete mode 100644 site/docs/documentation/01-priciples.de.md delete mode 100644 site/docs/documentation/02-architecture.de.md delete mode 100644 site/docs/documentation/03-openidoauth.de.md create mode 100644 site/docs/export-log create mode 100644 site/docs/flutter/00-overview.md create mode 100644 site/docs/go/00-overview.md delete mode 100644 site/docs/integrate/00-overview.de.md delete mode 100644 site/docs/integrate/01-singlepageapp.de.md delete mode 100644 site/docs/integrate/02-serverapp.de.md delete mode 100644 site/docs/integrate/03-mobileapp.de.md delete mode 100644 site/docs/integrate/04-nativeapp.de.md delete mode 100644 site/docs/integrate/05-proxy.de.md delete mode 100644 site/docs/integrate/06-api.de.md delete mode 100644 site/docs/integrate/07-products.de.md create mode 100644 site/docs/oauth2-proxy/00-overview.md create mode 100644 site/docs/oauth2-proxy/01-configure-zitadel.md create mode 100644 site/docs/oauth2-proxy/02-configure-proxy.md create mode 100644 site/docs/oauth2-proxy/03-end.md rename site/docs/{integrate/00-overview.en.md => quickstarts/00-overview.md} (67%) rename site/docs/{integrate/01-singlepageapp.en.md => quickstarts/01-singlepageapp.md} (90%) rename site/docs/{integrate/02-serverapp.en.md => quickstarts/02-serverapp.md} (100%) rename site/docs/{integrate/03-mobileapp.en.md => quickstarts/03-mobileapp.md} (100%) rename site/docs/{integrate/04-nativeapp.en.md => quickstarts/04-nativeapp.md} (100%) rename site/docs/{integrate/05-proxy.en.md => quickstarts/05-proxy.md} (100%) rename site/docs/{integrate/06-api.en.md => quickstarts/06-api.md} (100%) rename site/docs/{integrate/07-products.en.md => quickstarts/07-products.md} (100%) delete mode 100644 site/docs/start/00-quick-start.de.md delete mode 100644 site/docs/start/00-quick-start.en.md create mode 100644 site/docs/start/00-quick-start.md create mode 100644 site/docs/start/01-zitadel-ch.md create mode 100644 site/docs/start/02-orbos.md create mode 100644 site/docs/start/03-static-manifest.md delete mode 100644 site/docs/use/00-user.de.md rename site/docs/use/{00-user.en.md => 00-user.md} (94%) create mode 100644 site/docs/vue/00-overview.md create mode 100644 site/quickstarts/000-vue.md create mode 100644 site/quickstarts/100-angular.md create mode 100644 site/quickstarts/200-flutter.md create mode 100644 site/quickstarts/300-go.md create mode 100644 site/quickstarts/400-aspnet.md create mode 100644 site/quickstarts/500-OAuth2-Proxy.md delete mode 100644 site/src/components/CodeTable.svelte delete mode 100644 site/src/components/DocsHeader.svelte delete mode 100644 site/src/components/LanguageSwitcher.svelte delete mode 100644 site/src/i18n.js delete mode 100644 site/src/messages/de.json delete mode 100644 site/src/messages/en.json delete mode 100644 site/src/modules/cookie.js delete mode 100644 site/src/modules/language-store.js create mode 100644 site/src/routes/quickstarts/_quickstarts.js create mode 100644 site/src/routes/quickstarts/index.json.js create mode 100644 site/src/routes/quickstarts/index.svelte delete mode 100644 site/src/utils/generate_code_tabs.js create mode 100644 site/src/utils/highlight.js delete mode 100644 site/src/utils/language_extractor.js delete mode 100644 site/static/img/accounts_org_register.png create mode 100644 site/static/img/accounts_org_register_light.png create mode 100644 site/static/tech/android.svg create mode 100644 site/static/tech/angular.svg create mode 100644 site/static/tech/aspnet.png create mode 100644 site/static/tech/dart.svg create mode 100644 site/static/tech/flutter.svg create mode 100644 site/static/tech/golang.svg create mode 100644 site/static/tech/net.svg create mode 100644 site/static/tech/oauth2-proxy.svg create mode 100644 site/static/tech/react.png create mode 100644 site/static/tech/vue.svg diff --git a/.github/workflows/zitadel.yml b/.github/workflows/zitadel.yml deleted file mode 100644 index ff30ce0443..0000000000 --- a/.github/workflows/zitadel.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Zitadel Release -on: push - -env: - GITHUB_TOKEN: ${{ secrets.CR_PAT }} - REGISTRY: ghcr.io - NODE_VERSION: '12' - GO_VERSION: '1.15' - -jobs: - container: - runs-on: ubuntu-18.04 - steps: - - name: Source checkout - uses: actions/checkout@v2 - - name: Set output - id: branch - run: echo ::set-output name=short_ref::${GITHUB_REF#refs/*/} - - name: Check output - run: echo ${{ steps.branch.outputs.short_ref }} - - name: Generate Short SHA Container Tag - id: vars - run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short=12 HEAD)" - - name: Cache Docker layers - uses: actions/cache@v2 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ github.actor }} - password: ${{ secrets.CR_PAT }} - registry: ${{ env.REGISTRY }} - - uses: docker/build-push-action@v2 - with: - context: . - file: ./build/dockerfile - platforms: linux/amd64 - tags: ${{ env.REGISTRY }}/${{ github.repository }}:${{ steps.vars.outputs.sha_short }},${{ env.REGISTRY }}/${{ github.repository }}:${{ steps.branch.outputs.short_ref }} - push: true - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,mode=max,dest=/tmp/.buildx-cache - - release: - runs-on: ubuntu-18.04 - needs: [container] - env: - DOCKER_USERNAME: ${{ github.actor }} - DOCKER_PASSWORD: ${{ secrets.CR_PAT }} - steps: - - name: Source checkout - uses: actions/checkout@v2 - - name: Generate Short SHA Container Tag - id: vars - run: echo "::set-output name=sha_short::SHA-$(git rev-parse --short=12 HEAD)" - - name: Docker Login - run: docker login $REGISTRY -u $GITHUB_ACTOR -p $GITHUB_TOKEN - - name: Docker Pull short-sha - run: docker pull $REGISTRY/$GITHUB_REPOSITORY:${{ steps.vars.outputs.sha_short }} - - name: Semantic Release - id: semantic - uses: cycjimmy/semantic-release-action@v2 - with: - dry_run: false - semantic_version: 17.0.4 - - name: Do something when a new release published - if: steps.semantic.outputs.new_release_published == 'true' - run: | - echo ${{ steps.semantic.outputs.new_release_version }} - echo ${{ steps.semantic.outputs.new_release_major_version }} - echo ${{ steps.semantic.outputs.new_release_minor_version }} - echo ${{ steps.semantic.outputs.new_release_patch_version }} - - name: Docker Tag Version - run: docker tag $REGISTRY/$GITHUB_REPOSITORY:${{ steps.vars.outputs.sha_short }} $REGISTRY/$GITHUB_REPOSITORY:${{ steps.semantic.outputs.new_release_version }} - if: steps.semantic.outputs.new_release_published == 'true' - - name: Docker Tag Latest - run: docker tag $REGISTRY/$GITHUB_REPOSITORY:${{ steps.vars.outputs.sha_short }} $REGISTRY/$GITHUB_REPOSITORY:latest - if: steps.semantic.outputs.new_release_published == 'true' - - name: Docker Push Version - run: docker push $REGISTRY/$GITHUB_REPOSITORY:${{ steps.semantic.outputs.new_release_version }} - if: steps.semantic.outputs.new_release_published == 'true' - - name: Docker Push Latest - run: docker push $REGISTRY/$GITHUB_REPOSITORY:latest - if: steps.semantic.outputs.new_release_published == 'true' diff --git a/.releaserc.js b/.releaserc.js index 4e06dce043..13f1979713 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -6,15 +6,15 @@ module.exports = { ["@semantic-release/github", { "assets": [ { - "path": ".artifacts/zitadel-darwin-amd64/zitadelctl", + "path": "./artifacts/zitadelctl-darwin-amd64/zitadelctl-darwin-amd64", "label": "Zitadelctl Darwin x86_64" }, { - "path": ".artifacts/zitadel-linux-amd64/zitadelctl", + "path": "./artifacts/zitadelctl-linux-amd64/zitadelctl-linux-amd64", "label": "Zitadelctl Linux x86_64" }, { - "path": ".artifacts/zitadel-windows-amd64/zitadelctl", + "path": "./artifacts/zitadelctl-windows-amd64/zitadelctl-windows-amd64.exe", "label": "Zitadelctl Windows x86_64" } ] diff --git a/cmd/zitadel/setup.yaml b/cmd/zitadel/setup.yaml index ea627a4ddc..9b499f3612 100644 --- a/cmd/zitadel/setup.yaml +++ b/cmd/zitadel/setup.yaml @@ -88,7 +88,7 @@ SetUp: Step6: DefaultLabelPolicy: PrimaryColor: '#222324' - SecondaryColor: '#ffffff' + SecondaryColor: '#ffffff' Step7: OTP: true Step8: @@ -180,4 +180,4 @@ SetUp: Text: The domain {{.Domain}} has been claimed by an organisation. Your current user {{.Username}} is not part of this organisation. Therefore you'll have to change your email when you login. We have created a temporary username ({{.TempUsername}}) for this login. ButtonText: Login Step11: - MigrateV1EventstoreToV2: true + MigrateV1EventstoreToV2: $ZITADEL_MIGRATE_ES_V1 diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 4a62c52bc3..3038746589 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -17,6 +17,7 @@ Metrics: MeterName: 'github.com/caos/zitadel' AuthZ: + Domain: $ZITADEL_DEFAULT_DOMAIN Repository: Eventstore: ServiceName: 'AuthZ' @@ -186,6 +187,7 @@ API: Issuer: $ZITADEL_ISSUER DefaultLogoutRedirectURI: $ZITADEL_ACCOUNTS/logout/done CodeMethodS256: true + AuthMethodPrivateKeyJWT: true StorageConfig: DefaultLoginURL: $ZITADEL_ACCOUNTS/login?authRequestID= DefaultAccessTokenLifetime: 12h @@ -207,6 +209,9 @@ API: Token: Path: 'token' URL: '$ZITADEL_OAUTH/token' + Introspection: + Path: 'introspect' + URL: '$ZITADEL_OAUTH/introspect' EndSession: Path: 'endsession' URL: '$ZITADEL_AUTHORIZE/endsession' diff --git a/cmd/zitadel/system-defaults.yaml b/cmd/zitadel/system-defaults.yaml index b2ecfc023c..029925fa1a 100644 --- a/cmd/zitadel/system-defaults.yaml +++ b/cmd/zitadel/system-defaults.yaml @@ -45,6 +45,7 @@ SystemDefaults: IncludeDigits: true IncludeSymbols: false MachineKeySize: 2048 + ClientKeySize: 2048 Multifactors: OTP: Issuer: 'ZITADEL' diff --git a/console/package-lock.json b/console/package-lock.json index a60b752e27..0f2f97ae59 100644 --- a/console/package-lock.json +++ b/console/package-lock.json @@ -4,43 +4,34 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@angular-devkit/architect": { - "version": "0.1101.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1101.2.tgz", - "integrity": "sha512-MLmBfHiiyPhbFSSAX4oMecPjEuBauOui5uBpI6BKNnk/7783fznbkbAKjXlOco7M81gkNeEoHMR8c+mOfcvv7g==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.1.2", - "rxjs": "6.6.3" - } - }, "@angular-devkit/build-angular": { - "version": "0.1101.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1101.2.tgz", - "integrity": "sha512-EVJ7kAgy+sMnliCmHwN1niVeM7YaAvTMkF+ahImNfQRSQOW+QJ4F8vjiLtuARC6R02Yc5QPzELTHVPxC4Qll/A==", + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1102.0.tgz", + "integrity": "sha512-9Yl6qBOR1o0ThyLhrWJ6Rhr3qLRe+RP3sbfUt4QA+8wsIXN7WakEXJALlq7TDeG66OaWyyjbtC2q3EPdNeBC/g==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1101.2", - "@angular-devkit/build-optimizer": "0.1101.2", - "@angular-devkit/build-webpack": "0.1101.2", - "@angular-devkit/core": "11.1.2", + "@angular-devkit/architect": "0.1102.0", + "@angular-devkit/build-optimizer": "0.1102.0", + "@angular-devkit/build-webpack": "0.1102.0", + "@angular-devkit/core": "11.2.0", "@babel/core": "7.12.10", "@babel/generator": "7.12.11", + "@babel/plugin-transform-async-to-generator": "7.12.1", "@babel/plugin-transform-runtime": "7.12.10", "@babel/preset-env": "7.12.11", "@babel/runtime": "7.12.5", "@babel/template": "7.12.7", "@jsdevtools/coverage-istanbul-loader": "3.0.5", - "@ngtools/webpack": "11.1.2", + "@ngtools/webpack": "11.2.0", "ansi-colors": "4.1.1", - "autoprefixer": "10.2.1", + "autoprefixer": "10.2.4", "babel-loader": "8.2.2", "browserslist": "^4.9.1", "cacache": "15.0.5", "caniuse-lite": "^1.0.30001032", "circular-dependency-plugin": "5.2.2", "copy-webpack-plugin": "6.3.2", - "core-js": "3.8.2", + "core-js": "3.8.3", "critters": "0.0.6", "css-loader": "5.0.1", "cssnano": "4.1.10", @@ -51,14 +42,14 @@ "inquirer": "7.3.3", "jest-worker": "26.6.2", "karma-source-map-support": "1.4.0", - "less": "4.1.0", + "less": "4.1.1", "less-loader": "7.3.0", "license-webpack-plugin": "2.3.11", "loader-utils": "2.0.0", - "mini-css-extract-plugin": "1.3.3", + "mini-css-extract-plugin": "1.3.5", "minimatch": "3.0.4", - "open": "7.3.1", - "ora": "5.2.0", + "open": "7.4.0", + "ora": "5.3.0", "parse5-html-rewriting-stream": "6.0.1", "pnp-webpack-plugin": "1.6.4", "postcss": "8.2.4", @@ -68,38 +59,61 @@ "regenerator-runtime": "0.13.7", "resolve-url-loader": "3.1.2", "rimraf": "3.0.2", - "rollup": "2.36.1", + "rollup": "2.38.4", "rxjs": "6.6.3", - "sass": "1.32.4", + "sass": "1.32.6", "sass-loader": "10.1.1", "semver": "7.3.4", "source-map": "0.7.3", "source-map-loader": "1.1.3", "source-map-support": "0.5.19", - "speed-measure-webpack-plugin": "1.3.3", + "speed-measure-webpack-plugin": "1.4.2", "style-loader": "2.0.0", "stylus": "0.54.8", - "stylus-loader": "4.3.2", + "stylus-loader": "4.3.3", "terser": "5.5.1", "terser-webpack-plugin": "4.2.3", "text-table": "0.2.0", "tree-kill": "1.2.2", "webpack": "4.44.2", "webpack-dev-middleware": "3.7.2", - "webpack-dev-server": "3.11.1", + "webpack-dev-server": "3.11.2", "webpack-merge": "5.7.3", "webpack-sources": "2.2.0", "webpack-subresource-integrity": "1.5.2", "worker-plugin": "5.0.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "@angular-devkit/architect": { + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1102.0.tgz", + "integrity": "sha512-d9Da6SiTiDb5N1avxWLcPHSyWCq3G62TlROXxr32WkcQRko8wtgW5VOzgSkdmY2p6UTSME89naUojfzgu2Wh6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@angular-devkit/core": "11.2.0", + "rxjs": "6.6.3" + } + }, + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "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.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" } }, "@babel/core": { @@ -159,125 +173,170 @@ } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + }, + "dependencies": { + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + } } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + } } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz", + "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + }, + "dependencies": { + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + } } }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { @@ -292,39 +351,50 @@ } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - } } }, "agent-base": { @@ -336,14 +406,41 @@ "debug": "4" } }, + "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" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "autoprefixer": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.1.tgz", - "integrity": "sha512-dwP0UjyYvROUvtU+boBx8ff5pPWami1NGTrJs9YUsS/oZVbRAcdNHOOuXSA1fc46tgKqe072cVaKD69rvCc3QQ==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.4.tgz", + "integrity": "sha512-DCCdUQiMD+P/as8m3XkeTUkUKuuRqLGcwD0nll7wevhqoJfMRpJlkFd1+MQh1pvupjiQuip42lc/VFvfUTMSKw==", "dev": true, "requires": { "browserslist": "^4.16.1", - "caniuse-lite": "^1.0.30001173", + "caniuse-lite": "^1.0.30001181", "colorette": "^1.2.1", "fraction.js": "^4.0.13", "normalize-range": "^0.1.2", @@ -364,13 +461,28 @@ } }, "caniuse-lite": { - "version": "1.0.30001181", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001181.tgz", - "integrity": "sha512-m5ul/ARCX50JB8BSNM+oiPmQrR5UmngaQ3QThTTp5HcIIQGP/nPBs82BYLE+tigzm3VW+F4BJIhUyaVtEweelQ==", + "version": "1.0.30001187", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001187.tgz", + "integrity": "sha512-w7/EP1JRZ9552CyrThUnay2RkZ1DXxKe/Q2swTC4+LElLh9RRYrL1Z+27LlakB8kzY0fSmHw9mc7XYDUKAKWMA==", "dev": true } } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -381,9 +493,9 @@ } }, "electron-to-chromium": { - "version": "1.3.649", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.649.tgz", - "integrity": "sha512-ojGDupQ3UMkvPWcTICe4JYe17+o9OLiFMPoduoR72Zp2ILt1mRVeqnxBEd6s/ptekrnsFU+0A4lStfBe/wyG/A==", + "version": "1.3.664", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.664.tgz", + "integrity": "sha512-yb8LrTQXQnh9yhnaIHLk6CYugF/An50T20+X0h++hjjhVfgSp1DGoMSYycF8/aD5eiqS4QwaNhiduFvK8rifRg==", "dev": true }, "escalade": { @@ -392,6 +504,12 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -408,6 +526,44 @@ "integrity": "sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==", "dev": true }, + "open": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.0.tgz", + "integrity": "sha512-PGoBCX/lclIWlpS/R2PQuIR4NJoXh6X5AwVzE7WXnWRGvHg7+4TBCgsujUgiPpm0K1y4qvQeWnCWVTpTKZBtvA==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, "postcss": { "version": "8.2.4", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.4.tgz", @@ -450,13 +606,31 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, "@angular-devkit/build-optimizer": { - "version": "0.1101.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1101.2.tgz", - "integrity": "sha512-ARcUcEwaAR3n0gUq2hCx4eXONdnKKXTYSaw2GUHtraBDp+m/vFcE6Ufxyki453eHbHtaQ9yjXOcBqu86u1u8hA==", + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1102.0.tgz", + "integrity": "sha512-0Xb7xR0iUrbo/BnKJPTsy5IuodfFUWbvXUu9qeKc+oHzwxObX4Y32X0yyRpIJc5UK7Ce43HkopRQ/tHdoJp2fQ==", "dev": true, "requires": { "loader-utils": "2.0.0", @@ -487,29 +661,39 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.1101.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1101.2.tgz", - "integrity": "sha512-T8+LjKdxk1faQA4Dh3PYkRbIBLE6Tjv5SNZmdDXpvGoyxS9FmLgLDXQZqXkYgiAHwH6PxhoQX4iVxkHHFkkHog==", + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1102.0.tgz", + "integrity": "sha512-bfd6WlxcbWJ01HNNcFt4sBBaY+Bz2J/zFA6BSORvC5LpqawYCYeUbDJTY9Wb7gtp+aPEiNHI0HeUvoNkubsYfg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1101.2", - "@angular-devkit/core": "11.1.2", + "@angular-devkit/architect": "0.1102.0", + "@angular-devkit/core": "11.2.0", "rxjs": "6.6.3" - } - }, - "@angular-devkit/core": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.1.2.tgz", - "integrity": "sha512-V7zOMqL2l56JcwXVyswkG+7+t67r9XtkrVzRcG2Z5ZYwafU+iKWMwg5kBFZr1SX7fM1M9E4MpskxqtagQeUKng==", - "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.1102.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1102.0.tgz", + "integrity": "sha512-d9Da6SiTiDb5N1avxWLcPHSyWCq3G62TlROXxr32WkcQRko8wtgW5VOzgSkdmY2p6UTSME89naUojfzgu2Wh6g==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "rxjs": "6.6.3" + } + }, + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "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", @@ -531,14 +715,127 @@ } }, "@angular-devkit/schematics": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.1.2.tgz", - "integrity": "sha512-wIWI4+EPsjbN+23Rs0zE4GarKrUm8gMR3MpGAtlEmGG2ZsXEVdfiKUebBdWGrx0sEfgLN9JfePbZQFvqN5ifyw==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.2.0.tgz", + "integrity": "sha512-sMDacACJbA4pykiqgJf/RdW0damcf4mDqErGgEqs/bGG+SBUb8+wgt4cQnUwwVX5V2nMdvv7f0A84rgR6I3G2w==", "dev": true, "requires": { - "@angular-devkit/core": "11.1.2", - "ora": "5.2.0", + "@angular-devkit/core": "11.2.0", + "ora": "5.3.0", "rxjs": "6.6.3" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "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" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "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 + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "@angular/animations": { @@ -559,16 +856,16 @@ } }, "@angular/cli": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.1.2.tgz", - "integrity": "sha512-qOAkxCzBPm+QdXpSHxLERw1Vag8S0JHMY0zCwtG63XFvwHZCIihHRkOR3xHDWlVnGTmnUixg4Mt5s/4GGPuDJg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.2.0.tgz", + "integrity": "sha512-waIR5Nqc2wcYXZh/Mgm+4Iyvu0nzKAhvmKiJjcJ+f2UuPRMLdNAInTvhdpfgKaNdmiArxNa6UntRIu+EavGc9Q==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1101.2", - "@angular-devkit/core": "11.1.2", - "@angular-devkit/schematics": "11.1.2", - "@schematics/angular": "11.1.2", - "@schematics/update": "0.1101.2", + "@angular-devkit/architect": "0.1102.0", + "@angular-devkit/core": "11.2.0", + "@angular-devkit/schematics": "11.2.0", + "@schematics/angular": "11.2.0", + "@schematics/update": "0.1102.0", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "4.3.1", @@ -577,8 +874,9 @@ "jsonc-parser": "3.0.0", "npm-package-arg": "8.1.0", "npm-pick-manifest": "6.1.0", - "open": "7.3.1", - "pacote": "11.1.14", + "open": "7.4.0", + "ora": "5.3.0", + "pacote": "11.2.4", "resolve": "1.19.0", "rimraf": "3.0.2", "semver": "7.3.4", @@ -587,6 +885,81 @@ "uuid": "8.3.2" }, "dependencies": { + "@angular-devkit/architect": { + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1102.0.tgz", + "integrity": "sha512-d9Da6SiTiDb5N1avxWLcPHSyWCq3G62TlROXxr32WkcQRko8wtgW5VOzgSkdmY2p6UTSME89naUojfzgu2Wh6g==", + "dev": true, + "requires": { + "@angular-devkit/core": "11.2.0", + "rxjs": "6.6.3" + } + }, + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "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" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -596,12 +969,44 @@ "ms": "2.1.2" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true }, + "open": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.0.tgz", + "integrity": "sha512-PGoBCX/lclIWlpS/R2PQuIR4NJoXh6X5AwVzE7WXnWRGvHg7+4TBCgsujUgiPpm0K1y4qvQeWnCWVTpTKZBtvA==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "ora": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", + "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, "resolve": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", @@ -629,6 +1034,30 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "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 + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, @@ -831,9 +1260,9 @@ } }, "@angular/language-service": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-11.1.1.tgz", - "integrity": "sha512-87PYlTBBaMr0DYMYxkyeFas1qXIRYM0soNYkXC8yE+hxkGWTN15Zjk19+lx5z43++uNOiZw1mqnKTJoO46kE6A==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-11.2.0.tgz", + "integrity": "sha512-0n7yrBiwpN6qUEDBoMHxEzxRN+w4tIe+42Ra68UQ5leBx5mSA6P6Ipi+nPwy8RXjqNEHgR0D//30Zp18O8UhCA==", "dev": true }, "@angular/material": { @@ -894,9 +1323,9 @@ } }, "@babel/compat-data": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", - "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.13.tgz", + "integrity": "sha512-U/hshG5R+SIoW7HVWIdmy1cB7s3ki+r3FpyEZiCgpi4tFgPnX/vynY80ZGSASOIrUM6O7VxOgCZgdt7h97bUGg==", "dev": true }, "@babel/core": { @@ -961,12 +1390,12 @@ } }, "@babel/helper-annotate-as-pure": { - "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==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", + "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -976,9 +1405,9 @@ "dev": true }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -989,23 +1418,42 @@ } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", + "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-explode-assignable-expression": "^7.12.13", + "@babel/types": "^7.12.13" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-compilation-targets": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", - "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.16.tgz", + "integrity": "sha512-dBHNEEaZx7F3KoUYqagIhRIeqyyuI65xMndMZ3WwGwEBI609I4TleYQHcrS627vbKyNTXqShoN+fvYD9HuQxAg==", "dev": true, "requires": { - "@babel/compat-data": "^7.12.5", - "@babel/helper-validator-option": "^7.12.1", + "@babel/compat-data": "^7.12.13", + "@babel/helper-validator-option": "^7.12.16", "browserslist": "^4.14.5", "semver": "^5.5.0" }, @@ -1024,15 +1472,15 @@ } }, "caniuse-lite": { - "version": "1.0.30001181", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001181.tgz", - "integrity": "sha512-m5ul/ARCX50JB8BSNM+oiPmQrR5UmngaQ3QThTTp5HcIIQGP/nPBs82BYLE+tigzm3VW+F4BJIhUyaVtEweelQ==", + "version": "1.0.30001187", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001187.tgz", + "integrity": "sha512-w7/EP1JRZ9552CyrThUnay2RkZ1DXxKe/Q2swTC4+LElLh9RRYrL1Z+27LlakB8kzY0fSmHw9mc7XYDUKAKWMA==", "dev": true }, "electron-to-chromium": { - "version": "1.3.649", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.649.tgz", - "integrity": "sha512-ojGDupQ3UMkvPWcTICe4JYe17+o9OLiFMPoduoR72Zp2ILt1mRVeqnxBEd6s/ptekrnsFU+0A4lStfBe/wyG/A==", + "version": "1.3.664", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.664.tgz", + "integrity": "sha512-yb8LrTQXQnh9yhnaIHLk6CYugF/An50T20+X0h++hjjhVfgSp1DGoMSYycF8/aD5eiqS4QwaNhiduFvK8rifRg==", "dev": true }, "escalade": { @@ -1050,77 +1498,95 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.16.tgz", + "integrity": "sha512-KbSEj8l9zYkMVHpQqM3wJNxS1d9h3U9vm/uE5tpjMbaj3lTp+0noe3KPsV5dSD9jxKnf9jO9Ip9FX5PKNZCKow==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.12.16", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" - }, - "dependencies": { - "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - } + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -1130,76 +1596,54 @@ "dev": true }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11" - } - } } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1225,33 +1669,22 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", - "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.16.tgz", + "integrity": "sha512-jAcQ1biDYZBdaAxB4yg46/XirgX7jBDiMHDbwYQOgtViLBXGxJpZQ24jutmBqAIB/q+AwB6j+NbBXjKxEY8vqg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-annotate-as-pure": "^7.12.13", "regexpu-core": "^4.7.1" } }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, "@babel/helper-explode-assignable-expression": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", - "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.13.tgz", + "integrity": "sha512-5loeRNvMo9mx1dA/d6yNi+YiKziJZFylZnCo1nmFF4qPU4yJ14abhWESuSMQSlQxWdxdOFzxXjk/PpfudTtYyw==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -1261,9 +1694,9 @@ "dev": true }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1327,12 +1760,31 @@ } }, "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.12.13.tgz", + "integrity": "sha512-KSC5XSj5HreRhYQtZ3cnSnQwDzgnbdUDEFsxkN0m6Q3WrCRt72xrnZ8+h+pX7YxM7hr87zIO3a/v5p/H3TrnVw==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/types": "^7.12.13" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-member-expression-to-functions": { @@ -1411,20 +1863,20 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz", + "integrity": "sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA==", "dev": true }, "@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.13.tgz", + "integrity": "sha512-Qa6PU9vNcj1NZacZZI1Mvwt+gXDH6CTfgAkSjeRMLE8HxtDK76+YDId6NQR+z7Rgd5arhD2cIbS74r0SxD6PDA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-wrap-function": "^7.12.13", + "@babel/types": "^7.12.13" }, "dependencies": { "@babel/helper-validator-identifier": { @@ -1434,9 +1886,9 @@ "dev": true }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1517,9 +1969,9 @@ "dev": true }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1545,78 +1997,148 @@ "dev": true }, "@babel/helper-validator-option": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz", - "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.16.tgz", + "integrity": "sha512-uCgsDBPUQDvzr11ePPo4TVEocxj8RXjUVSC/Y8N1YpVAI/XDdUwGJu78xmlGhTxj2ntaWM7n9LQdRtyhOzT2YQ==", "dev": true }, "@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.13.tgz", + "integrity": "sha512-t0aZFEmBJ1LojdtJnhOaQEVejnzYhyjWHSsNSNo8vOYRbAJNh6r6GQF7pd36SqG7OKGbn+AewVQ/0IfYfIuGdw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } + }, + "@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -1682,136 +2204,136 @@ "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz", - "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.13.tgz", + "integrity": "sha512-1KH46Hx4WqP77f978+5Ye/VUbuwQld2hph70yaw2hXS2v7ER2f3nlpNMu909HO2rbvP0NKLlMVDPh9KXklVMhA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-remap-async-to-generator": "^7.12.13", "@babel/plugin-syntax-async-generators": "^7.8.0" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.13.tgz", + "integrity": "sha512-8SCJ0Ddrpwv4T7Gwb33EmW1V9PY5lggTO+A8WjyIwxrSHDUyBw4MtF96ifn1n8H806YlxbVCoKXbbmzD6RD+cA==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", - "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.16.tgz", + "integrity": "sha512-yiDkYFapVxNOCcBfLnsb/qdsliroM+vc3LHiZwS4gh7pFjo5Xq3BDhYBNn3H3ao+hWPvqeeTdU+s+FIvokov+w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-dynamic-import": "^7.8.0" } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", - "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", - "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.13.tgz", + "integrity": "sha512-v9eEi4GiORDg8x+Dmi5r8ibOe0VXoKDeNPYcTTxdGN4eOWikrJfDJCJrr1l5gKGvsNyGJbrfMftC2dTL6oz7pg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-json-strings": "^7.8.0" } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", - "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.13.tgz", + "integrity": "sha512-fqmiD3Lz7jVdK6kabeSr1PZlWSUVqSitmHEe3Z00dtGTKieWnX9beafvavc32kjORa5Bai4QNHgFDwWJP+WtSQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", - "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.13.tgz", + "integrity": "sha512-Qoxpy+OxhDBI5kRqliJFAl4uWXk3Bn24WeFstPH0iLymFehSAUR8MHpqU7njyXv/qbo7oN6yTy5bfCmXdKpo1Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", - "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", + "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.13.tgz", + "integrity": "sha512-WvA1okB/0OS/N3Ldb3sziSrXg6sRphsBgqiccfcQq7woEn5wQLNX82Oc4PlaFcdwcWHuQXAtb8ftbS8Fbsg/sg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "@babel/plugin-transform-parameters": "^7.12.13" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", - "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.13.tgz", + "integrity": "sha512-9+MIm6msl9sHWg58NvqpNpLtuFbmpFYk37x8kgnGzAHvX35E1FyAwSUt5hIkSoWJFSAH+iwU8bJ4fcD1zKXOzg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", - "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.16.tgz", + "integrity": "sha512-O3ohPwOhkwji5Mckb7F/PJpJVJY3DpPsrt/F0Bk40+QMk9QpAIqeGusHWqu/mYqsM8oBa6TziL/2mbERWsUZjg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", - "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.13.tgz", + "integrity": "sha512-sV0V57uUwpauixvR7s2o75LmwJI6JECwm5oPUY5beZB1nBl2i37hc7CJGqB5G+58fur5Y6ugvl3LRONk5x34rg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", - "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", + "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-async-generators": { @@ -1824,12 +2346,12 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-dynamic-import": { @@ -1914,21 +2436,21 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", - "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.13.tgz", + "integrity": "sha512-tBtuN6qtCTd+iHzVZVOMNp+L04iIJBpqkdY42tWbmjIT5wvR2kx7gxMBsyhQtFzHwBbyGi9h8J8r9HgnOpQHxg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-async-to-generator": { @@ -1943,12 +2465,12 @@ }, "dependencies": { "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -1958,9 +2480,9 @@ "dev": true }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1971,98 +2493,115 @@ } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", - "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", + "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz", - "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz", + "integrity": "sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-classes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", - "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.13.tgz", + "integrity": "sha512-cqZlMlhCC1rVnxE5ZGMtIb896ijL90xppMiuWXcwcOAuFczynpd3KYemb91XFFPi3wJSe/OcrX9lXoowatkkxA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", "globals": "^11.1.0" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" - }, - "dependencies": { - "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - } + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -2072,76 +2611,54 @@ "dev": true }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11" - } - } } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -2167,276 +2684,350 @@ } }, "@babel/plugin-transform-computed-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", - "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.13.tgz", + "integrity": "sha512-dDfuROUPGK1mTtLKyDPUavmj2b6kFu82SmgpztBFEO974KMjJT+Ytj3/oWsTUMBmgPcp9J5Pc1SlcAYRpJ2hRA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-destructuring": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", - "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.13.tgz", + "integrity": "sha512-Dn83KykIFzjhA3FDPA1z4N+yfF3btDGhjnJwxIj0T43tP0flCujnU8fKgEkf0C1biIpSv9NZegPBQ1J6jYkwvQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", - "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", + "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", - "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", + "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", - "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", + "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-for-of": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", - "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.13.tgz", + "integrity": "sha512-xCbdgSzXYmHGyVX3+BsQjcd4hv4vA/FDy7Kc8eOpzKmBBPEOTurt0w5fCRQaGl+GSBORKgJdstQ1rHl4jbNseQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-function-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", - "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", + "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", - "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", - "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", - "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/highlight": "^7.12.13" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - }, - "@babel/helper-replace-supers": { + "@babel/helper-validator-identifier": { "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11" - } + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/plugin-transform-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", + "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", + "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.13.tgz", + "integrity": "sha512-JHLOU0o81m5UqG0Ulz/fPC68/v+UTuGTWaZBUwpEk1fYQ1D9LfKV6MPn4ttJKqRo5Lm460fkzjLTL4EHvCprvA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/generator": { + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - } } }, "debug": { @@ -2457,193 +3048,191 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", - "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.13.tgz", + "integrity": "sha512-OGQoeVXVi1259HjuoDnsQMlMkT9UkZT9TpXAsqWplS/M0N1g3TJAn/ByOCeQu7mfjc5WpSsRU+jV1Hd89ts0kQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", "babel-plugin-dynamic-import-node": "^2.3.3" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - } } }, "debug": { @@ -2664,194 +3253,192 @@ } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", - "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.13.tgz", + "integrity": "sha512-aHfVjhZ8QekaNF/5aNdStCGzwTbU7SI5hUybBKlMzqIMC7w7Ho8hx5a4R/DkTHfRfLwHGGxSpFt9BfxKCoXKoA==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-hoist-variables": "^7.12.13", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", "babel-plugin-dynamic-import-node": "^2.3.3" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - } } }, "debug": { @@ -2872,191 +3459,189 @@ } }, "@babel/plugin-transform-modules-umd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", - "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.13.tgz", + "integrity": "sha512-BgZndyABRML4z6ibpi7Z98m4EVLFI9tVsZDADC14AElFaNHHBcJIovflJ6wtCqFxwy2YJ1tJhGRsr0yLPKoN+w==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - } } }, "debug": { @@ -3077,110 +3662,110 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", - "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", + "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1" + "@babel/helper-create-regexp-features-plugin": "^7.12.13" } }, "@babel/plugin-transform-new-target": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", - "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", + "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-object-super": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", - "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", + "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13" }, "dependencies": { "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.16.tgz", + "integrity": "sha512-zYoZC1uvebBFmj1wFAlXwt35JLEgecefATtKp20xalwEK8vHAixLBXTGxNrVGEmTT+gzOThUgr8UEdgtalc1BQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.12.13" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -3190,54 +3775,54 @@ "dev": true }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.12.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.16.tgz", + "integrity": "sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw==", "dev": true }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -3263,39 +3848,39 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", - "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.13.tgz", + "integrity": "sha512-e7QqwZalNiBRHCpJg/P8s/VJeSRYgmtWySs1JwvfwPqhBbiWfOcHDKdeAi6oAyIimoKWBlwc8oTgbZHdhCoVZA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-property-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", - "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", + "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-regenerator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", - "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz", + "integrity": "sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", - "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", + "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-runtime": { @@ -3310,12 +3895,12 @@ }, "dependencies": { "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -3325,9 +3910,9 @@ "dev": true }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -3338,68 +3923,68 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", - "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", + "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", - "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.13.tgz", + "integrity": "sha512-dUCrqPIowjqk5pXsx1zPftSq4sT0aCeZVAxhdgs3AMgyaDmoUT0G+5h3Dzja27t76aUEIJWlFgPJqJ/d4dbTtg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", - "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", + "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-template-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", - "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.13.tgz", + "integrity": "sha512-arIKlWYUgmNsF28EyfmiQHJLJFlAJNYkuQO10jL46ggjBpeb2re1P9K9YGxNJB45BqTbaslVysXDYm/g3sN/Qg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-typeof-symbol": { - "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==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", + "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", - "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", + "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", - "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", + "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/preset-env": { @@ -3477,12 +4062,12 @@ }, "dependencies": { "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -3492,9 +4077,9 @@ "dev": true }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -3614,9 +4199,9 @@ } }, "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, "@jsdevtools/coverage-istanbul-loader": { @@ -3633,14 +4218,47 @@ } }, "@ngtools/webpack": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.1.2.tgz", - "integrity": "sha512-x/HVx4doKu4gAwGGk+C89JCFe5GF8Te7I7uvwMTqEXr+Ua9YHYvN/q2IwLdhIXPB4ilBSIjrb9zm05yBvBTAeg==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.2.0.tgz", + "integrity": "sha512-2KYaA/fIw863jydXDolWp4kUNLODVIKs2834vW+oztocnYFe/z4dTz6rArKFUCixMCu12ieXrjAetzGA564bsg==", "dev": true, "requires": { - "@angular-devkit/core": "11.1.2", - "enhanced-resolve": "5.6.0", + "@angular-devkit/core": "11.2.0", + "enhanced-resolve": "5.7.0", "webpack-sources": "2.2.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "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": { @@ -3692,9 +4310,9 @@ "dev": true }, "@npmcli/git": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.4.tgz", - "integrity": "sha512-OJZCmJ9DNn1cz9HPXXsPmUBnqaArot3CGYo63CyajHQk+g87rPXVOJByGsskQJhPsUUEXJcsZ2Q6bWd2jSwnBA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.6.tgz", + "integrity": "sha512-a1MnTfeRPBaKbFY07fd+6HugY1WAkKJzdiJvlRub/9o5xz2F/JtPacZZapx5zRJUQFIzSL677vmTSxEcDMrDbg==", "dev": true, "requires": { "@npmcli/promise-spawn": "^1.1.0", @@ -3702,7 +4320,7 @@ "mkdirp": "^1.0.3", "npm-pick-manifest": "^6.0.0", "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", + "promise-retry": "^2.0.1", "semver": "^7.3.2", "unique-filename": "^1.1.1", "which": "^2.0.2" @@ -3714,6 +4332,16 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, "semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", @@ -3735,15 +4363,13 @@ } }, "@npmcli/installed-package-contents": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.5.tgz", - "integrity": "sha512-aKIwguaaqb6ViwSOFytniGvLPb9SMCUm39TgM3SfUo7n0TxUMbwoXfpwyvQ4blm10lzbAwTsvjr7QZ85LvTi4A==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", "dev": true, "requires": { "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1", - "read-package-json-fast": "^1.1.1", - "readdir-scoped-modules": "^1.1.0" + "npm-normalize-package-bin": "^1.0.1" } }, "@npmcli/move-file": { @@ -3774,9 +4400,9 @@ } }, "@npmcli/node-gyp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.1.tgz", - "integrity": "sha512-pBqoKPWmuk9iaEcXlLBVRIA6I1kG9JiICU+sG0NuD6NAR461F+02elHJS4WkQxHW2W5rnsfvP/ClKwmsZ9RaaA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz", + "integrity": "sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg==", "dev": true }, "@npmcli/promise-spawn": { @@ -3789,17 +4415,29 @@ } }, "@npmcli/run-script": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.1.tgz", - "integrity": "sha512-G8c86g9cQHyRINosIcpovzv0BkXQc3urhL1ORf3KTe4TS4UBsg2O4Z2feca/W3pfzdHEJzc83ETBW4aKbb3SaA==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.3.tgz", + "integrity": "sha512-ELPGWAVU/xyU+A+H3pEPj0QOvYwLTX71RArXcClFzeiyJ/b/McsZ+d0QxpznvfFtZzxGN/gz/1cvlqICR4/suQ==", "dev": true, "requires": { - "@npmcli/node-gyp": "^1.0.0", - "@npmcli/promise-spawn": "^1.3.0", + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", "infer-owner": "^1.0.4", "node-gyp": "^7.1.0", "puka": "^1.0.1", - "read-package-json-fast": "^1.1.3" + "read-package-json-fast": "^2.0.1" + }, + "dependencies": { + "read-package-json-fast": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.1.tgz", + "integrity": "sha512-bp6z0tdgLy9KzdfENDIw/53HWAolOVoQTRWXv7PUiqAo3YvvoUVeLr7RWPWq+mu7KUOu9kiT4DvxhUgNUBsvug==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + } } }, "@protobufjs/aspromise": { @@ -3857,32 +4495,90 @@ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, "@schematics/angular": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.1.2.tgz", - "integrity": "sha512-ZhaB/QBwfWsvZYJplLH8VK/7vnFpUbk1nptjC106K/I38xlmWdB4pStLJK94eyJk0KlItnsPvE0a9KuLPKA/Ow==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.2.0.tgz", + "integrity": "sha512-PtbyZ7TEEEae9Y5siSZYigWyk8iOSjZ10ThA7tRxm8gdcLjGimyyKr5TyjufIAvrXIYnBXNLgPkZG6s5CQIEyw==", "dev": true, "requires": { - "@angular-devkit/core": "11.1.2", - "@angular-devkit/schematics": "11.1.2", + "@angular-devkit/core": "11.2.0", + "@angular-devkit/schematics": "11.2.0", "jsonc-parser": "3.0.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "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 + } } }, "@schematics/update": { - "version": "0.1101.2", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1101.2.tgz", - "integrity": "sha512-WwMsIkhR3hq7gvfB5HsuTK2Sz9iZmpz0/AiFYRJbX+mXL/KgONcridyorW45Dg1Q2sRXVGiAUxWsQYzjmeLO7g==", + "version": "0.1102.0", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1102.0.tgz", + "integrity": "sha512-2hFt/2iPe6LqQvzYj4HvQ8us0e0lBU75rSK2yY6VfiZWR/qo7yk99YKI7JWFTLvLsNbhNnSG/9opXJHqqUoc3g==", "dev": true, "requires": { - "@angular-devkit/core": "11.1.2", - "@angular-devkit/schematics": "11.1.2", + "@angular-devkit/core": "11.2.0", + "@angular-devkit/schematics": "11.2.0", "@yarnpkg/lockfile": "1.1.0", "ini": "2.0.0", "npm-package-arg": "^8.0.0", - "pacote": "11.1.14", + "pacote": "11.2.4", "semver": "7.3.4", "semver-intersect": "1.4.0" }, "dependencies": { + "@angular-devkit/core": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.2.0.tgz", + "integrity": "sha512-qqYEH8m/bwpngoLDMFuth8ykvoHxQ3aHHnAWfRXz9NXydwSfathG0VSYCctB126sK39JKIn+xq16CQAExxNu+Q==", + "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" + } + }, "ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", @@ -3897,6 +4593,12 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "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 } } }, @@ -3926,9 +4628,9 @@ "dev": true }, "@types/bytebuffer": { - "version": "5.0.41", - "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.41.tgz", - "integrity": "sha512-Mdrv4YcaHvpkx25ksqqFaezktx3yZRcd51GZY0rY/9avyaqZdiT/GiWRhfrJhMpgzXqTOSHgGvsumGxJFNiZZA==", + "version": "5.0.42", + "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.42.tgz", + "integrity": "sha512-lEgKojWUAc/MG2t649oZS5AfYFP2xRNPoDuwDBlBMjHXd8MaGPgFgtCXUK7inZdBOygmVf10qxc1Us8GXC96aw==", "requires": { "@types/long": "*", "@types/node": "*" @@ -3953,9 +4655,9 @@ "dev": true }, "@types/cors": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.9.tgz", - "integrity": "sha512-zurD1ibz21BRlAOIKP8yhrxlqKx6L9VCwkB5kMiP6nZAhoF5MvC7qS1qPA7nRcr1GJolfkQC7/EAL4hdYejLtg==", + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", + "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", "dev": true }, "@types/file-saver": { @@ -4026,9 +4728,9 @@ "dev": true }, "@types/node": { - "version": "14.14.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", - "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" + "version": "14.14.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.28.tgz", + "integrity": "sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -4328,9 +5030,9 @@ } }, "agentkeepalive": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.3.tgz", - "integrity": "sha512-wn8fw19xKZwdGPO47jivonaHRTd+nGOMP1z11sgGeQzDy2xd5FG0R67dIMcKHDE2cJ5y+YXV30XVGUBPRSY7Hg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", + "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", "dev": true, "requires": { "debug": "^4.1.0", @@ -4589,12 +5291,6 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, "ascli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", @@ -5923,12 +6619,12 @@ "dev": true }, "copy-anything": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.1.tgz", - "integrity": "sha512-lA57e7viQHOdPQcrytv5jFeudZZOXuyk47lZym279FiDQ8jeZomXiGuVf6ffMKkJ+3TIai3J1J3yi6M+/4U35g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.3.tgz", + "integrity": "sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ==", "dev": true, "requires": { - "is-what": "^3.7.1" + "is-what": "^3.12.0" } }, "copy-concurrently": { @@ -6015,9 +6711,9 @@ } }, "core-js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", - "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz", + "integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==", "dev": true }, "core-js-compat": { @@ -6044,15 +6740,15 @@ } }, "caniuse-lite": { - "version": "1.0.30001181", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001181.tgz", - "integrity": "sha512-m5ul/ARCX50JB8BSNM+oiPmQrR5UmngaQ3QThTTp5HcIIQGP/nPBs82BYLE+tigzm3VW+F4BJIhUyaVtEweelQ==", + "version": "1.0.30001187", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001187.tgz", + "integrity": "sha512-w7/EP1JRZ9552CyrThUnay2RkZ1DXxKe/Q2swTC4+LElLh9RRYrL1Z+27LlakB8kzY0fSmHw9mc7XYDUKAKWMA==", "dev": true }, "electron-to-chromium": { - "version": "1.3.649", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.649.tgz", - "integrity": "sha512-ojGDupQ3UMkvPWcTICe4JYe17+o9OLiFMPoduoR72Zp2ILt1mRVeqnxBEd6s/ptekrnsFU+0A4lStfBe/wyG/A==", + "version": "1.3.664", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.664.tgz", + "integrity": "sha512-yb8LrTQXQnh9yhnaIHLk6CYugF/An50T20+X0h++hjjhVfgSp1DGoMSYycF8/aD5eiqS4QwaNhiduFvK8rifRg==", "dev": true }, "escalade": { @@ -6339,9 +7035,9 @@ "dev": true }, "postcss": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.4.tgz", - "integrity": "sha512-kRFftRoExRVXZlwUuay9iC824qmXPcQQVzAjbCCgjpXnkdMCJYBu2gTwAaFBzv8ewND6O8xFb3aELmEkh9zTzg==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.6.tgz", + "integrity": "sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==", "dev": true, "requires": { "colorette": "^1.2.1", @@ -6645,12 +7341,6 @@ "ms": "^2.1.1" } }, - "debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=", - "dev": true - }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -6862,16 +7552,6 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, - "dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -7093,18 +7773,18 @@ "dev": true }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" }, "dependencies": { "bn.js": { @@ -7165,9 +7845,9 @@ } }, "engine.io": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.0.tgz", - "integrity": "sha512-vW7EAtn0HDQ4MtT5QbmCHF17TaYLONv2/JwdYsq9USPRZVM4zG7WB3k0Nc321z8EuSOlhGokrYlYx4176QhD0A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.1.tgz", + "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -7195,9 +7875,9 @@ } }, "ws": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", - "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", + "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==", "dev": true } } @@ -7212,9 +7892,9 @@ } }, "enhanced-resolve": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.6.0.tgz", - "integrity": "sha512-C3GGDfFZmqUa21o10YRKbZN60DPl0HyXKXxoEnQMWso9u7KMU23L7CBHfr/rVxORddY/8YQZaU2MZ1ewTS8Pcw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", + "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -7222,9 +7902,9 @@ }, "dependencies": { "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true } } @@ -7248,9 +7928,9 @@ "dev": true }, "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", "dev": true }, "errno": { @@ -7614,9 +8294,9 @@ }, "dependencies": { "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.2.0.tgz", + "integrity": "sha512-M/u37b4oSGlusaU8ZB96BfFPWQ8MbsZYXB+kXGMiDj6IKinkcNaQvmirBuWj8mAXqP6LYn1rQvbTYum3yPhaOA==", "dev": true } } @@ -8221,9 +8901,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.0.tgz", - "integrity": "sha512-M11rgtQp5GZMZzDL7jLTNxbDfurpzuau5uqRWDPvlHjfvg3TdScAZo96GLvhMjImrmR8uAt0FS2RLoMrfWGKlg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -8367,15 +9047,15 @@ "dev": true }, "grpc": { - "version": "1.24.3", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.3.tgz", - "integrity": "sha512-EDemzuZTfhM0hgrXqC4PtR76O3t+hTIYJYR5vgiW0yt2WJqo4mhxUqZUirzUQz34Psz7dbLp38C6Cl7Ij2vXRQ==", + "version": "1.24.5", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.5.tgz", + "integrity": "sha512-+dY6lfLPovblJO5QitBQM2L67efI5JjBzCqJQURSINPzoFHos+5bs4DHwtes7BF+dkx5eN3fx/VUFRCmWTsE7g==", "requires": { "@types/bytebuffer": "^5.0.40", "lodash.camelcase": "^4.3.0", "lodash.clone": "^4.5.0", "nan": "^2.13.2", - "node-pre-gyp": "^0.15.0", + "node-pre-gyp": "^0.16.0", "protobufjs": "^5.0.3" }, "dependencies": { @@ -8544,10 +9224,13 @@ } }, "hosted-git-info": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", - "dev": true + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", + "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "hpack.js": { "version": "2.1.6", @@ -9429,11 +10112,12 @@ } }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", "dev": true, "requires": { + "call-bind": "^1.0.2", "has-symbols": "^1.0.1" } }, @@ -9873,9 +10557,9 @@ } }, "karma": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.0.3.tgz", - "integrity": "sha512-dmiLQdsNAvnbV1G6VvUK7Cl5xpwiMisZNT8MjBtOo49jKlnZSWLxQIemuLT8sGSzvx5IGgMfMQEtf/CALiUEVQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.1.1.tgz", + "integrity": "sha512-vVDFxFGAsclgmFjZA/qGw5xqWdZIWxVD7xLyCukYUYd5xs/uGzYbXGOT5zOruVBQleKEmXIr4H2hzGCTn+M9Cg==", "dev": true, "requires": { "body-parser": "^1.19.0", @@ -9896,7 +10580,7 @@ "qjobs": "^1.2.0", "range-parser": "^1.2.1", "rimraf": "^3.0.2", - "socket.io": "^3.0.4", + "socket.io": "^3.1.0", "source-map": "^0.6.1", "tmp": "0.2.1", "ua-parser-js": "^0.7.23", @@ -9951,9 +10635,9 @@ "dev": true }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "is-fullwidth-code-point": { @@ -10039,9 +10723,9 @@ } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.5", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.5.tgz", + "integrity": "sha512-jYRGS3zWy20NtDtK2kBgo/TlAoy5YUuhD9/LZ7z7W4j1Fdw2cqD0xEEclf8fxc8xjD6X5Qr+qQQwCEsP8iRiYg==", "dev": true } } @@ -10111,9 +10795,9 @@ "dev": true }, "known-css-properties": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.20.0.tgz", - "integrity": "sha512-URvsjaA9ypfreqJ2/ylDr5MUERhJZ+DhguoWRr2xgS5C7aGCalXo+ewL+GixgKBfhT2vuL02nbIgNGqVWgTOYw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.21.0.tgz", + "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", "dev": true }, "lcid": { @@ -10125,9 +10809,9 @@ } }, "less": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.0.tgz", - "integrity": "sha512-w1Ag/f34g7LwtQ/sMVSGWIyZx+gG9ZOAEtyxeX1fG75is6BMyC2lD5kG+1RueX7PkAvlQBm2Lf2aN2j0JbVr2A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz", + "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==", "dev": true, "requires": { "copy-anything": "^2.0.1", @@ -10444,9 +11128,9 @@ "dev": true }, "make-fetch-happen": { - "version": "8.0.13", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.13.tgz", - "integrity": "sha512-rQ5NijwwdU8tIaBrpTtSVrNCcAJfyDRcKBC76vOQlyJX588/88+TE+UpjWl4BgG7gCkp29wER7xcRqkeg+x64Q==", + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", + "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", "dev": true, "requires": { "agentkeepalive": "^4.1.3", @@ -10461,7 +11145,7 @@ "minipass-fetch": "^1.3.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "promise-retry": "^1.1.1", + "promise-retry": "^2.0.1", "socks-proxy-agent": "^5.0.0", "ssri": "^8.0.0" }, @@ -10493,6 +11177,16 @@ "agent-base": "6", "debug": "4" } + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } } } }, @@ -10535,9 +11229,9 @@ } }, "mdast-util-from-markdown": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.4.tgz", - "integrity": "sha512-jj891B5pV2r63n2kBTFh8cRI2uR9LQHsXG1zSDqfhXkIlDzrTcIlbB5+5aaYEkl8vOPIOPLf8VT7Ere1wWTMdw==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", "dev": true, "requires": { "@types/mdast": "^3.0.0", @@ -10548,9 +11242,9 @@ } }, "mdast-util-to-markdown": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.2.tgz", - "integrity": "sha512-iRczns6WMvu0hUw02LXsPDJshBIwtUPbvHBWo19IQeU0YqmzlA8Pd30U8V7uiI0VPkxzS7A/NXBXH6u+HS87Zg==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -10641,46 +11335,6 @@ "yargs-parser": "^20.2.3" }, "dependencies": { - "hosted-git-info": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", - "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "normalize-package-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", - "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", - "dev": true, - "requires": { - "hosted-git-info": "^3.0.6", - "resolve": "^1.17.0", - "semver": "^7.3.2", - "validate-npm-package-license": "^3.0.1" - } - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "type-fest": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", @@ -10688,9 +11342,9 @@ "dev": true }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.5", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.5.tgz", + "integrity": "sha512-jYRGS3zWy20NtDtK2kBgo/TlAoy5YUuhD9/LZ7z7W4j1Fdw2cqD0xEEclf8fxc8xjD6X5Qr+qQQwCEsP8iRiYg==", "dev": true } } @@ -10729,9 +11383,9 @@ "dev": true }, "micromark": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.2.tgz", - "integrity": "sha512-IXuP76p2uj8uMg4FQc1cRE7lPCLsfAXuEfdjtdO55VRiFO1asrCSQ5g43NmPqFtRwzEnEhafRVzn2jg0UiKArQ==", + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", "dev": true, "requires": { "debug": "^4.0.0", @@ -10811,9 +11465,9 @@ "dev": true }, "mini-css-extract-plugin": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.3.tgz", - "integrity": "sha512-7lvliDSMiuZc81kI+5/qxvn47SCM7BehXex3f2c6l/pR3Goj58IQxZh9nuPQ3AkGQgoETyXuIqLDaO5Oa0TyBw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.5.tgz", + "integrity": "sha512-tvmzcwqJJXau4OQE5vT72pRT18o2zF+tQJp8CWchqvfQnTlflkzS+dANYcRdyPRWUWRkfmeNTKltx0NZI/b5dQ==", "dev": true, "requires": { "loader-utils": "^2.0.0", @@ -11101,9 +11755,9 @@ } }, "needle": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", - "integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", + "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", "requires": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", @@ -11331,9 +11985,9 @@ } }, "node-pre-gyp": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", - "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.16.0.tgz", + "integrity": "sha512-4efGA+X/YXAHLi1hN8KaPrILULaUn2nWecFrn1k2I+99HpoyvcOGEbtcOxpDiUwPF2ZANMJDh32qwOUPenuR1g==", "requires": { "detect-libc": "^1.0.2", "mkdirp": "^0.5.3", @@ -11419,15 +12073,45 @@ } }, "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "normalize-path": { @@ -11763,16 +12447,6 @@ "mimic-fn": "^2.1.0" } }, - "open": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/open/-/open-7.3.1.tgz", - "integrity": "sha512-f2wt9DCBKKjlFbjzGb8MOAW8LH8F0mrs1zc7KTjAJ9PZNQbfenzWbNP1VZJvw6ICMG9r14Ah6yfwPn7T7i646A==", - "dev": true, - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - } - }, "opn": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", @@ -11795,88 +12469,6 @@ "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" }, - "ora": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.2.0.tgz", - "integrity": "sha512-+wG2v8TUU8EgzPHun1k/n45pXquQ9fHnbXVetl9rRgO6kjZszGGbraF3XPTIdgeA+s1lbRjSEftAnyT0w8ZMvQ==", - "dev": true, - "requires": { - "bl": "^4.0.3", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", @@ -11966,9 +12558,9 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "pacote": { - "version": "11.1.14", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.1.14.tgz", - "integrity": "sha512-6c5OhQelaJFDfiw/Zd8MfGCvvFHurSdeGzufZMPvRFImdbNOYFciOINf3DtUNUaU3h98eCb749UyHDsgvL19+A==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.2.4.tgz", + "integrity": "sha512-GfTeVQGJ6WyBQbQD4t3ocHbyOmTQLmWjkCKSZPmKiGFKYKNUaM5U2gbLzUW8WG1XmS9yQFnsTFA0k3o1+q4klQ==", "dev": true, "requires": { "@npmcli/git": "^2.0.1", @@ -13051,6 +13643,12 @@ "retry": "^0.10.0" }, "dependencies": { + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, "retry": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", @@ -13758,9 +14356,9 @@ } }, "read-package-json-fast": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-1.2.1.tgz", - "integrity": "sha512-OFbpwnHcv74Oa5YN5WvbOBfLw6yPmPcwvyJJw/tj9cWFBF7juQUDLDSZiOjEcgzfweWeeROOmbPpNN1qm4hcRg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-1.2.2.tgz", + "integrity": "sha512-39DbPJjkltEzfXJXB6D8/Ir3GFOU2YbSKa2HaB/Y3nKrc/zY+0XrALpID6/13ezWyzqvOHrBbR4t4cjQuTdBVQ==", "dev": true, "requires": { "json-parse-even-better-errors": "^2.3.0", @@ -13779,6 +14377,24 @@ "type-fest": "^0.6.0" }, "dependencies": { + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", @@ -13833,18 +14449,6 @@ "util-deprecate": "^1.0.1" } }, - "readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, "readdirp": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", @@ -14292,12 +14896,21 @@ } }, "rollup": { - "version": "2.36.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.36.1.tgz", - "integrity": "sha512-eAfqho8dyzuVvrGqpR0ITgEdq0zG2QJeWYh+HeuTbpcaXk8vNFc48B7bJa1xYosTCKx0CuW+447oQOW8HgBIZQ==", + "version": "2.38.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.4.tgz", + "integrity": "sha512-B0LcJhjiwKkTl79aGVF/u5KdzsH8IylVfV56Ut6c9ouWLJcUK17T83aZBetNYSnZtXf2OHD4+2PbmRW+Fp5ulg==", "dev": true, "requires": { - "fsevents": "~2.1.2" + "fsevents": "~2.3.1" + }, + "dependencies": { + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + } } }, "run-async": { @@ -14356,9 +14969,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.32.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.4.tgz", - "integrity": "sha512-N0BT0PI/t3+gD8jKa83zJJUb7ssfQnRRfqN+GIErokW6U4guBpfYl8qYB+OFLEho+QvnV5ZH1R9qhUC/Z2Ch9w==", + "version": "1.32.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.6.tgz", + "integrity": "sha512-1bcDHDcSqeFtMr0JXI3xc/CXX6c4p0wHHivJdru8W7waM7a1WjKMm4m/Z5sY7CbVw4Whi2Chpcw6DFfSWwGLzQ==", "dev": true, "requires": { "chokidar": ">=2.0.0 <4.0.0" @@ -14933,9 +15546,9 @@ } }, "socket.io": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.0.tgz", - "integrity": "sha512-Aqg2dlRh6xSJvRYK31ksG65q4kmBOqU4g+1ukhPcoT6wNGYoIwSYPlCPuRwOO9pgLUajojGFztl6+V2opmKcww==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.1.tgz", + "integrity": "sha512-7cBWdsDC7bbyEF6WbBqffjizc/H4YF1wLdZoOzuYfo2uMNSFjJKuQ36t0H40o9B20DO6p+mSytEd92oP4S15bA==", "dev": true, "requires": { "@types/cookie": "^0.4.0", @@ -15143,9 +15756,9 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true }, "sourcemap-codec": { @@ -15155,9 +15768,9 @@ "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -15165,15 +15778,15 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -15181,9 +15794,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", "dev": true }, "spdy": { @@ -15242,12 +15855,63 @@ "dev": true }, "speed-measure-webpack-plugin": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz", - "integrity": "sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.4.2.tgz", + "integrity": "sha512-AtVzD0bnIy2/B0fWqJpJgmhcrfWFhBlduzSo0uwplr/QvB33ZNZj2NEth3NONgdnZJqicK0W0mSxnLSbsVCDbw==", "dev": true, "requires": { - "chalk": "^2.0.1" + "chalk": "^4.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "split-string": { @@ -15599,9 +16263,9 @@ } }, "stylelint": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.9.0.tgz", - "integrity": "sha512-VVWH2oixOAxpWL1vH+V42ReCzBjW2AeqskSAbi8+3OjV1Xg3VZkmTcAqBZfRRvJeF4BvYuDLXebW3tIHxgZDEg==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.10.0.tgz", + "integrity": "sha512-eDuLrL0wzPKbl5/TbNGZcbw0lTIGbDEr5W6lCODvb1gAg0ncbgCRt7oU0C2VFDvbrcY0A3MFZOwltwTRmc0XCw==", "dev": true, "requires": { "@stylelint/postcss-css-in-js": "^0.37.2", @@ -15623,7 +16287,7 @@ "ignore": "^5.1.8", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", - "known-css-properties": "^0.20.0", + "known-css-properties": "^0.21.0", "lodash": "^4.17.20", "log-symbols": "^4.0.0", "mathml-tag-names": "^2.1.3", @@ -15669,12 +16333,6 @@ "color-convert": "^2.0.1" } }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -15709,34 +16367,6 @@ "ms": "2.1.2" } }, - "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "globby": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", - "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -15876,9 +16506,9 @@ } }, "stylelint-scss": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.18.0.tgz", - "integrity": "sha512-LD7+hv/6/ApNGt7+nR/50ft7cezKP2HM5rI8avIdGaUWre3xlHfV4jKO/DRZhscfuN+Ewy9FMhcTq0CcS0C/SA==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.19.0.tgz", + "integrity": "sha512-Ic5bsmpS4wVucOw44doC1Yi9f5qbeVL4wPFiEOaUElgsOuLEN6Ofn/krKI8BeNL2gAn53Zu+IcVV4E345r6rBw==", "dev": true, "requires": { "lodash": "^4.17.15", @@ -15886,25 +16516,6 @@ "postcss-resolve-nested-selector": "^0.1.1", "postcss-selector-parser": "^6.0.2", "postcss-value-parser": "^4.1.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", - "dev": true - } } }, "stylus": { @@ -15959,9 +16570,9 @@ } }, "stylus-loader": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.3.2.tgz", - "integrity": "sha512-xXVKHY+J7GBlOmqjCL1VvQfc+pFkBdWGtcpJSvBGE49nWWHaukox7KCjRdLTEzjrmHODm4+rLpqkYWzfJteMXQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.3.3.tgz", + "integrity": "sha512-PpWB5PnCXUzW4WMYhCvNzAHJBjIBPMXwsdfkkKuA9W7k8OQFMl/19/AQvaWsxz2IptxUlCseyJ6TY/eEKJ4+UQ==", "dev": true, "requires": { "fast-glob": "^3.2.4", @@ -16060,9 +16671,9 @@ }, "dependencies": { "ajv": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", - "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.0.tgz", + "integrity": "sha512-svS9uILze/cXbH0z2myCK2Brqprx/+JJYK5pHicT/GQiBfzzhUVAIT6MwqJg8y4xV/zoGsUeuPuwtoiKSGE15g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -16556,15 +17167,15 @@ } }, "typescript": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", - "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.7.tgz", + "integrity": "sha512-yi7M4y74SWvYbnazbn8/bmJmX4Zlej39ZOqwG/8dut/MYoSQ119GY9ZFbbGsD4PFZYWxqik/XsP3vk3+W5H3og==", "dev": true }, "ua-parser-js": { - "version": "0.7.23", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.23.tgz", - "integrity": "sha512-m4hvMLxgGHXG3O3fQVAyyAQpZzDOvwnhOTjYz5Xmr7r/+LpkNy3vJXdVRWgd1TkAb7NGROZuSy96CrlNVjA7KA==", + "version": "0.7.24", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.24.tgz", + "integrity": "sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==", "dev": true }, "unicode-canonical-property-names-ecmascript": { @@ -17667,9 +18278,9 @@ } }, "webpack-dev-server": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz", - "integrity": "sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ==", + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz", + "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -18289,9 +18900,9 @@ "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" }, "yallist": { "version": "4.0.0", diff --git a/console/package.json b/console/package.json index 042903a453..31e162618b 100644 --- a/console/package.json +++ b/console/package.json @@ -33,7 +33,7 @@ "file-saver": "^2.0.5", "google-proto-files": "^2.4.0", "google-protobuf": "^3.13.0", - "grpc": "^1.24.3", + "grpc": "^1.24.5", "grpc-web": "^1.2.1", "moment": "^2.29.1", "ngx-quicklink": "^0.2.6", @@ -44,28 +44,28 @@ "zone.js": "~0.11.3" }, "devDependencies": { - "@angular/cli": "~11.1.2", - "@angular-devkit/build-angular": "~0.1101.2", + "@angular/cli": "~11.2.0", + "@angular-devkit/build-angular": "~0.1102.0", "@angular/compiler-cli": "~11.0.0", "@types/jasmine": "~3.6.3", - "@angular/language-service": "~11.1.1", + "@angular/language-service": "~11.2.0", "@types/jasminewd2": "~2.0.3", - "@types/node": "^14.14.22", + "@types/node": "^14.14.28", "codelyzer": "^6.0.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~6.0.0", - "karma": "~6.0.3", + "karma": "~6.1.1", "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "prettier": "^2.2.1", "protractor": "~7.0.0", - "stylelint": "^13.9.0", + "stylelint": "^13.10.0", "stylelint-config-standard": "^20.0.0", - "stylelint-scss": "^3.18.0", + "stylelint-scss": "^3.19.0", "ts-node": "~9.1.1", "tslint": "~6.1.3", - "typescript": "^4.0.5" + "typescript": "^4.0.7" } } diff --git a/console/src/app/app-routing.module.ts b/console/src/app/app-routing.module.ts index 4947ee464d..2db6941e5d 100644 --- a/console/src/app/app-routing.module.ts +++ b/console/src/app/app-routing.module.ts @@ -11,6 +11,15 @@ const routes: Routes = [ loadChildren: () => import('./pages/home/home.module').then(m => m.HomeModule), canActivate: [AuthGuard], }, + { + path: 'firststeps', + loadChildren: () => import('./modules/onboarding/onboarding.module') + .then(m => m.OnboardingModule), + canActivate: [AuthGuard, RoleGuard], + data: { + roles: ['iam.write'], + } + }, { path: 'granted-projects', loadChildren: () => import('./pages/projects/granted-projects/granted-projects.module') diff --git a/console/src/app/app.component.html b/console/src/app/app.component.html index dd61103f61..d6178a4a97 100644 --- a/console/src/app/app.component.html +++ b/console/src/app/app.component.html @@ -1,19 +1,25 @@ - - + + src="../assets/images/zitadel-logo-solo-light.svg" /> - + - @@ -37,8 +43,8 @@ - + diff --git a/console/src/app/modules/card/card.component.scss b/console/src/app/modules/card/card.component.scss index 273957e649..6654a3925c 100644 --- a/console/src/app/modules/card/card.component.scss +++ b/console/src/app/modules/card/card.component.scss @@ -24,6 +24,8 @@ font-size: 16px; text-transform: uppercase; letter-spacing: .05em; + text-overflow: ellipsis; + overflow: hidden; } .fill-space { diff --git a/console/src/app/modules/card/card.component.ts b/console/src/app/modules/card/card.component.ts index 9fd2a15c8e..98b4a65e21 100644 --- a/console/src/app/modules/card/card.component.ts +++ b/console/src/app/modules/card/card.component.ts @@ -18,7 +18,7 @@ import { Component, Input } from '@angular/core'; ], }) export class CardComponent { - public expanded: boolean = true; + @Input() public expanded: boolean = true; @Input() public title: string = ''; @Input() public description: string = ''; @Input() public animate: boolean = false; diff --git a/console/src/app/modules/idp-create/idp-create.component.ts b/console/src/app/modules/idp-create/idp-create.component.ts index 223aee16ce..4cd6bf2302 100644 --- a/console/src/app/modules/idp-create/idp-create.component.ts +++ b/console/src/app/modules/idp-create/idp-create.component.ts @@ -110,9 +110,9 @@ export class IdpCreateComponent implements OnInit, OnDestroy { setTimeout(() => { this.loading = false; this.router.navigate([ - this.serviceType === PolicyComponentServiceType.MGMT ? 'org' : - this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam' : '', - 'idp', idp.getId()]); + (this.serviceType === PolicyComponentServiceType.MGMT ? 'org' : + this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam' : ''), + 'policy', 'login']); }, 2000); }).catch(error => { this.toast.showError(error); diff --git a/console/src/app/modules/idp-table/idp-table.component.html b/console/src/app/modules/idp-table/idp-table.component.html index 510c8f4522..2e9cb06291 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -1,6 +1,6 @@ - +
diff --git a/console/src/app/modules/info-section/info-section.component.html b/console/src/app/modules/info-section/info-section.component.html index 90564546d6..0ca31edc24 100644 --- a/console/src/app/modules/info-section/info-section.component.html +++ b/console/src/app/modules/info-section/info-section.component.html @@ -1,5 +1,7 @@ -
- +
+ + +
diff --git a/console/src/app/modules/info-section/info-section.component.scss b/console/src/app/modules/info-section/info-section.component.scss index ee75019945..0bc8c21f38 100644 --- a/console/src/app/modules/info-section/info-section.component.scss +++ b/console/src/app/modules/info-section/info-section.component.scss @@ -7,24 +7,36 @@ .info-section-row { display: flex; - background-color: if($is-dark-theme, #ffffff13, #f3f3f3); border-radius: 4px; padding: .5rem 0; padding-right: 1rem; - color: if($is-dark-theme, #d6d6d6, #3c4257); font-size: 14px; .icon { - margin-right: 1rem; - height: 1.2rem; - line-height: 1.2rem; - font-size: 1.2rem; - margin-left: .5rem; - color: $primary-color; + margin-right: 1rem; + height: 1.2rem; + line-height: 1.2rem; + font-size: 1.2rem; + margin-left: .5rem; } + .info-section-content { - flex: 1; + flex: 1; + } + + &.info { + background-color: if($is-dark-theme, #4f566b, #cbf4c9); + color: if($is-dark-theme, #cbf4c9, #0e6245); + + .icon { + color: $primary-color; + } + } + + &.warn { + background-color: if($is-dark-theme, #4f566b, #ffc1c1); + color: if($is-dark-theme, #ffc1c1, #620e0e); } } } diff --git a/console/src/app/modules/info-section/info-section.component.ts b/console/src/app/modules/info-section/info-section.component.ts index 999364e46e..c6c093648e 100644 --- a/console/src/app/modules/info-section/info-section.component.ts +++ b/console/src/app/modules/info-section/info-section.component.ts @@ -1,8 +1,16 @@ -import { Component } from '@angular/core'; +import { Component, Input } from '@angular/core'; + +enum InfoSectionType { + INFO = 'INFO', + WARN = 'WARN', +} @Component({ selector: 'cnsl-info-section', templateUrl: './info-section.component.html', styleUrls: ['./info-section.component.scss'], }) -export class InfoSectionComponent { } +export class InfoSectionComponent { + + @Input() type = InfoSectionType.INFO; +} diff --git a/console/src/app/modules/onboarding/onboarding-routing.module.ts b/console/src/app/modules/onboarding/onboarding-routing.module.ts new file mode 100644 index 0000000000..459446304f --- /dev/null +++ b/console/src/app/modules/onboarding/onboarding-routing.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { OnboardingComponent } from './onboarding.component'; + +const routes: Routes = [ + { + path: '', + component: OnboardingComponent, + data: { animation: 'AddPage' }, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class OnboardingRoutingModule { } diff --git a/console/src/app/modules/onboarding/onboarding.component.html b/console/src/app/modules/onboarding/onboarding.component.html new file mode 100644 index 0000000000..e11a82996d --- /dev/null +++ b/console/src/app/modules/onboarding/onboarding.component.html @@ -0,0 +1,27 @@ +
+
+

{{'ONBOARDING.HEADER' | translate}}

+

{{'ONBOARDING.TITLE' | translate}}

+
+
+

{{'ONBOARDING.DESCRIPTION' | translate}}

+ +

{{'ONBOARDING.STEPS_TITLE' | translate}}

+
+ +
+

{{step.titleI18nKey | translate}}

+

{{step.descI18nKey | translate}}

+
+ + +
+
+
\ No newline at end of file diff --git a/console/src/app/modules/onboarding/onboarding.component.scss b/console/src/app/modules/onboarding/onboarding.component.scss new file mode 100644 index 0000000000..d58ff345dd --- /dev/null +++ b/console/src/app/modules/onboarding/onboarding.component.scss @@ -0,0 +1,147 @@ +@import '~@angular/material/theming'; + +@mixin onboarding-theme($theme) { + /* stylelint-disable */ + $primary: map-get($theme, primary); + $primary-color: mat-color($primary, 500); + $is-dark-theme: map-get($theme, is-dark); + /* stylelint-enable */ + + .onboarding-row { + box-shadow: inset 0 -1px if($is-dark-theme, #303131, #e3e8ee); + + .prev { + background: $primary-color; + } + + .goto { + text-decoration: none; + background: white; + border: 1px solid if($is-dark-theme, #303131, #e3e8ee); + + &.docs { + background-color: $primary-color; + + i { + font-size: 1rem; + margin-left: 3px; + } + } + } + } +} + +.split { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + flex-direction: column; + box-sizing: border-box; + border-radius: 0.5rem; + box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%); + + @media only screen and (min-width: 1024px) { + flex-direction: row; + + .right { + overflow: auto; + } + } + + .left { + flex-basis: 300px; + box-sizing: border-box; + padding: 1.5rem; + background: linear-gradient(40deg, rgb(80, 66, 121),rgb(177, 59, 122),rgb(225,53,81), rgb(230,107,86)); + box-shadow: inset -2px 1px 15px -9px #000000; + + h1 { + color: white; + } + + .firststeps { + color: #fad6e3; + text-transform: uppercase; + font-size: 12px; + font-weight: bold; + } + + p { + color: #fad6e3; + font-size: 12px; + font-weight: bold; + } + + button { + width: 100%; + } + } + + .right { + padding: 1.5rem; + flex: 1; + box-sizing: border-box; + + .desc { + color: var(--grey); + font-size: 20px; + margin-top: .5rem; + } + + .onboarding-row { + display: flex; + padding: 1rem 0; + align-items: center; + + .prev { + height: 40px; + width: 40px; + min-width: 40px; + border-radius: .5rem; + display: flex; + align-items: center; + justify-content: center; + margin-right: 2rem; + color: white; + font-size: 1.2rem; + box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%); + } + + h3 { + margin-top: 0; + margin-bottom: .5rem; + font-size: 15px; + } + + p { + font-size: 12px; + margin: 0; + color: var(--grey); + } + + .fill-space { + flex: 1; + } + + .action-row { + display: flex; + align-items: center; + flex-wrap: wrap; + justify-content: flex-end; + + .goto { + background-color: white; + padding: 2px 1rem; + color: black; + border-radius: 50vw; + font-size: 12px; + margin: .5rem 0 .5rem 1rem; + white-space: nowrap; + } + } + } + } +} \ No newline at end of file diff --git a/console/src/app/modules/onboarding/onboarding.component.spec.ts b/console/src/app/modules/onboarding/onboarding.component.spec.ts new file mode 100644 index 0000000000..9361e6bb35 --- /dev/null +++ b/console/src/app/modules/onboarding/onboarding.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OnboardingComponent } from './onboarding.component'; + +describe('OnboardingComponent', () => { + let component: OnboardingComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ OnboardingComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(OnboardingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/onboarding/onboarding.component.ts b/console/src/app/modules/onboarding/onboarding.component.ts new file mode 100644 index 0000000000..3091e94299 --- /dev/null +++ b/console/src/app/modules/onboarding/onboarding.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import { AuthenticationService } from 'src/app/services/authentication.service'; + +@Component({ + selector: 'cnsl-onboarding', + templateUrl: './onboarding.component.html', + styleUrls: ['./onboarding.component.scss'] +}) +export class OnboardingComponent { + public steps = [ + { titleI18nKey: 'ONBOARDING.STEPS.1.TITLE', descI18nKey: 'ONBOARDING.STEPS.1.DESC', docs: "https://docs.zitadel.ch/use", link: ['/projects', 'create'] }, + { titleI18nKey: 'ONBOARDING.STEPS.2.TITLE', descI18nKey: 'ONBOARDING.STEPS.2.DESC', docs: "https://docs.zitadel.ch/use", link: ['/projects'] }, + { titleI18nKey: 'ONBOARDING.STEPS.3.TITLE', descI18nKey: 'ONBOARDING.STEPS.3.DESC', link: ['/iam', 'policies'] }, + ]; + constructor() { } +} diff --git a/console/src/app/modules/onboarding/onboarding.module.ts b/console/src/app/modules/onboarding/onboarding.module.ts new file mode 100644 index 0000000000..2ba9318ca8 --- /dev/null +++ b/console/src/app/modules/onboarding/onboarding.module.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { TranslateModule } from '@ngx-translate/core'; + +import { OnboardingRoutingModule } from './onboarding-routing.module'; +import { OnboardingComponent } from './onboarding.component'; + +@NgModule({ + declarations: [OnboardingComponent], + imports: [ + CommonModule, + TranslateModule, + OnboardingRoutingModule, + MatButtonModule, + ], +}) +export class OnboardingModule { } 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 e88404bf39..930f2f869d 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 @@ -73,14 +73,14 @@

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

+ [disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false">

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

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

+ [disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false"> diff --git a/console/src/app/pages/home/home.component.html b/console/src/app/pages/home/home.component.html index dd89e0074c..5aba76393c 100644 --- a/console/src/app/pages/home/home.component.html +++ b/console/src/app/pages/home/home.component.html @@ -16,12 +16,42 @@
+ + +

{{'ONBOARDING.HEADER' | translate}}

+

{{'ONBOARDING.TITLE' | translate}}

+

{{'ONBOARDING.DESCRIPTION' | translate}}

+ +
+
+ + +

{{'HOME.QUICKSTARTS.LABEL' | translate}}

+

{{'HOME.QUICKSTARTS.TITLE' | translate}}

+

{{'HOME.QUICKSTARTS.DESCRIPTION' | translate}}

+
+ + + + +
+ +
+

- {{'HOME.IAM'| translate}}

+ {{'HOME.IAM'| translate}} +

{{'HOME.IAM_DESC'| translate}}

@@ -30,15 +60,12 @@ class="las la-link">
- {{'HOME.IAM_POLICY_IAM' | translate}} - {{'HOME.IAM_POLICY_COMPLEXITY' | translate}} - {{'HOME.IAM_POLICY_LOGIN' | translate}} + {{'HOME.IAM_POLICY_IAM' | + translate}} + {{'HOME.IAM_POLICY_COMPLEXITY' + | translate}} + {{'HOME.IAM_POLICY_LOGIN' | + translate}} @@ -59,8 +86,8 @@ @@ -69,18 +96,18 @@

- {{'HOME.PROJECTS'| translate}}

+ {{'HOME.PROJECTS'| translate}} +

{{'HOME.PROJECTS_DESC'| translate}}

- {{'HOME.PROJECTS_NEW_LINK' | translate}} + {{'HOME.PROJECTS_NEW_LINK' | + translate}}
@@ -92,23 +119,21 @@ {{'HOME.PROTECTION'| translate}}

{{'HOME.PROTECTION_DESC'| translate}}

- {{'HOME.ORG_POLICY_IAM' | translate}} + {{'HOME.ORG_POLICY_IAM' | + translate}} {{'HOME.ORG_POLICY_COMPLEXITY' | translate}} - {{'HOME.ORG_POLICY_LOGIN' | translate}} + [routerLink]="[ '/org', 'policy','complexity']">{{'HOME.ORG_POLICY_COMPLEXITY' | + translate}} + {{'HOME.ORG_POLICY_LOGIN' | + translate}}
@@ -118,15 +143,14 @@

- {{'HOME.USERS'| translate}}

+ {{'HOME.USERS'| translate}} +

{{'HOME.USERS_DESC'| translate}}

- {{'HOME.USERS_HUMANS' | translate}} - {{'HOME.USERS_MACHINES' | translate}} + {{'HOME.USERS_HUMANS' | + translate}} + {{'HOME.USERS_MACHINES' | + translate}} {{'HOME.USERS_CREATE' | translate}} diff --git a/console/src/app/pages/home/home.component.scss b/console/src/app/pages/home/home.component.scss index af6a5c076a..091d2f1ea8 100644 --- a/console/src/app/pages/home/home.component.scss +++ b/console/src/app/pages/home/home.component.scss @@ -1,6 +1,7 @@ .wrapper { padding-bottom: 100px; + position: relative; .header { display: flex; @@ -31,6 +32,78 @@ margin: -1rem; justify-content: space-evenly; + .onboard, + .quickstart { + text-decoration: none; + cursor: pointer; + box-sizing: border-box; + flex: 1 0 45%; + position: relative; + border-radius: 0.5rem; + margin: 1rem; + padding: 1.5rem; + box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%); + + h2 { + color: white; + } + + .first-steps { + text-transform: uppercase; + font-size: 12px; + font-weight: bold; + } + + .close { + visibility: hidden; + position: absolute; + top: 0; + right: 0; + + i { + color: white; + font-size: 1rem; + } + } + + &:hover { + .close { + visibility: visible; + } + } + } + + .onboard { + background: linear-gradient(40deg, rgb(80, 66, 121),rgb(177, 59, 122),rgb(225,53,81), rgb(230,107,86)); + + p { + color: #fad6e3; + } + } + + .quickstart { + background: linear-gradient(30deg, #2283a6,#6c8f59); + + p { + color: #d6f3fa; + } + + .logo-cloud { + display: flex; + flex-wrap: wrap; + margin: -0.5rem; + + i { + font-size: 40px; + padding: .5rem; + border: 1px solid #ffffff50; + border-radius: .5rem; + margin: 0.5rem; + color: white; + } + } + } + .item { flex: 1 0 45%; margin: 0 1rem; diff --git a/console/src/app/pages/home/home.component.ts b/console/src/app/pages/home/home.component.ts index 71a65d38cf..e9cbc25f4b 100644 --- a/console/src/app/pages/home/home.component.ts +++ b/console/src/app/pages/home/home.component.ts @@ -8,8 +8,26 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; }) export class HomeComponent { public dark: boolean = true; + public firstStepsDismissed: boolean = false; + public quickstartsDismissed: boolean = false; + constructor(public authService: GrpcAuthService) { const theme = localStorage.getItem('theme'); this.dark = theme === 'dark-theme' ? true : theme === 'light-theme' ? false : true; + + this.firstStepsDismissed = localStorage.getItem('firstStartDismissed') == 'true' ? true : false; + this.quickstartsDismissed = localStorage.getItem('quickstartsDismissed') == 'true' ? true : false; + } + + dismissFirstSteps(event: Event): void { + event.preventDefault(); + localStorage.setItem('firstStartDismissed', 'true'); + this.firstStepsDismissed = true; + } + + dismissQuickstarts(event: Event): void { + event.preventDefault(); + localStorage.setItem('quickstartsDismissed', 'true'); + this.firstStepsDismissed = true; } } diff --git a/console/src/app/pages/home/home.module.ts b/console/src/app/pages/home/home.module.ts index 2d397bd3fb..535d247a14 100644 --- a/console/src/app/pages/home/home.module.ts +++ b/console/src/app/pages/home/home.module.ts @@ -1,19 +1,20 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MatRippleModule } from '@angular/material/core'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { AvatarModule } from 'src/app/modules/avatar/avatar.module'; import { CardModule } from 'src/app/modules/card/card.module'; +import { OnboardingModule } from 'src/app/modules/onboarding/onboarding.module'; import { SharedModule } from 'src/app/modules/shared/shared.module'; import { HomeRoutingModule } from './home-routing.module'; import { HomeComponent } from './home.component'; - - @NgModule({ declarations: [HomeComponent], imports: [ @@ -24,9 +25,12 @@ import { HomeComponent } from './home.component'; MatButtonModule, TranslateModule, AvatarModule, + MatTooltipModule, SharedModule, MatProgressSpinnerModule, CardModule, + MatRippleModule, + OnboardingModule, ], }) export class HomeModule { } diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.html b/console/src/app/pages/projects/apps/app-create/app-create.component.html index c43900be68..567e85ddb2 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.html +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.html @@ -23,18 +23,14 @@

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

{{ 'APP.NAME' | translate }} - + {{'PROJECT.APP.NAMEREQUIRED' | translate}}

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

- - -
{{('APP.OIDC.APPTYPE'+type.key.toString()) | translate}}
-
-
+ +
@@ -42,37 +38,19 @@ - - {{'APP.OIDC.RESPONSESECTION' | translate}} -
- - {{'APP.OIDC.RESPONSE'+responsetype.type | translate}} - -
-
- - -
-
- - +
{{'APP.OIDC.AUTHMETHODSECTION' | translate}} - - - {{'APP.OIDC.AUTHMETHOD'+authmethod.type | translate}} - - + + +
- +
@@ -89,25 +67,10 @@

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

- - - {{ 'APP.OIDC.REDIRECT' | translate }} - - - - - - - {{uri}} cancel - - - -

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

+ +

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

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

-
- - {{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }} - - - - - - - {{uri}} cancel - - - -

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

+ +
@@ -171,9 +118,8 @@ [ {{'APP.OIDC.GRANT'+element | translate}} - {{i < oidcApp.grantTypesList.length - 1 ? ', ': ''}} - ] - + {{i < oidcApp.grantTypesList.length - 1 ? ', ' : '' }} ] +
@@ -182,9 +128,8 @@ [ {{('APP.OIDC.RESPONSE'+element | translate)}} - {{i < oidcApp.responseTypesList.length - 1 ? ', ': ''}} - ] - + {{i < oidcApp.responseTypesList.length - 1 ? ', ' : '' }} ] +
@@ -205,9 +150,8 @@ [ {{redirect}} - {{i < oidcApp.redirectUrisList.length - 1 ? ', ': ''}} - ] - + {{i < oidcApp.redirectUrisList.length - 1 ? ', ' : '' }} ] +
@@ -217,15 +161,14 @@ [ {{redirect}} - {{i < oidcApp.postLogoutRedirectUrisList.length - 1 ? ', ': ''}} - ] - + {{i < oidcApp.postLogoutRedirectUrisList.length - 1 ? ', ' : '' }} ] +
- +
@@ -241,8 +184,8 @@ {{ 'APP.OIDC.APPTYPE' | translate }} - - {{ 'APP.OIDC.APPTYPE'+type | translate }} + + {{ 'APP.OIDC.APPTYPE'+type.type | translate }} @@ -274,32 +217,18 @@ - - {{ 'APP.OIDC.REDIRECT' | translate }} - - - {{uri}} cancel - - - - - - - {{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }} - - - {{uri}} cancel - - - - +
+ + + + +
+ + + + + + +

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

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

@@ -12,52 +34,85 @@ {{errorMessage}} - -
-
- - - {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}} - - - {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}} - - + +
+ + + {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}} + + + {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}} + + - - {{ 'APP.NAME' | translate }} - - - - -

Discovery Endpoint: {{docs.discoveryEndpoint}}

-

Issuer: {{docs.issuer}}

-
-
-
- -
- - - - -
- + + {{ 'APP.NAME' | translate }} + +
+ + + + +
+ +
    +
  • + {{problem.localizedMessage}}
  • +
+
+
+ +
+

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

+ + + {{ 'APP.OIDC.DEVMODE' | translate }} + + + + {{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}} + + +

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

+ +
+ + + + + + +
+ + + + +
+ +
+ +
-
- -
    -
  • - {{problem.localizedMessage}}
  • -
-
-
-
{{ 'APP.OIDC.CLIENTID' | translate }} @@ -141,89 +196,46 @@ {{'APP.OIDC.CLOCKSKEW' | translate}} - - -
- -

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

- - - {{ 'APP.OIDC.DEVMODE' | translate }} - - - - {{'APP.OIDC.DEVMODEDESC' | translate}} - - - - {{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}} - - -

- {{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}

- - - - {{ 'APP.OIDC.REDIRECT' | translate }} - - - - - - - - {{redirect}} - cancel - - - -

- {{'APP.OIDC.REDIRECTNOTVALID' | translate}}

- -
- - {{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }} - - - - - - - {{redirect}} - cancel - - - -

- {{'APP.OIDC.REDIRECTNOTVALID' | translate}}

-
+ - +
+ +
+ + +
+
{{'APP.PAGES.NEXTSTEPS.TITLE' | translate}}
+
+
+
{{ 'APP.PAGES.NEXTSTEPS.0.TITLE' | translate }}
+

{{'APP.PAGES.NEXTSTEPS.0.DESC' | translate}}

+ +
- - - +
+
{{ 'APP.PAGES.NEXTSTEPS.1.TITLE' | translate }}
+

{{'APP.PAGES.NEXTSTEPS.1.DESC' | translate}}

+ + +
+
+
{{ 'APP.PAGES.NEXTSTEPS.2.TITLE' | translate }}
+

{{'APP.PAGES.NEXTSTEPS.2.DESC' | 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 db7946ac71..894f809cca 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 @@ -8,11 +8,21 @@ display: block; } - h1 { - font-size: 1.2rem; - margin: 0 1rem; + .title-col { margin-left: 2rem; - font-weight: normal; + margin-right: 1rem; + min-width: 200px; + + h1 { + font-size: 1.2rem; + margin: 0 0 0 0; + font-weight: normal; + } + + span { + font-size: 12px; + color: var(--grey); + } } .desc { @@ -20,6 +30,7 @@ display: block; font-size: .9rem; color: var(--grey); + margin-top: 1.5rem; } .zitadel-warning { @@ -41,10 +52,10 @@ display: flex; flex-direction: row; flex-wrap: wrap; + align-items: center; .toggle { outline: none; - align-self: flex-start; margin-top: 1rem; margin-right: 1rem; border-radius: .5rem; @@ -91,6 +102,11 @@ align-items: center; } + .redirect-section { + flex: 1; + margin: 0 .5rem; + } + .formfield { flex: 1 1 30%; margin: 0 .5rem; @@ -99,27 +115,9 @@ flex-basis: 100%; } } - - .chiplist { - margin-bottom: 1rem; - } - - .chip-form { - width: 100%; - display: flex; - align-items: center; - - .formfield { - flex: 1; - } - - button { - margin-top: 1rem; - } - } - + .section-title { - margin-bottom: 1.5rem; + margin: 1.5rem 0 0 0; } .full-width { @@ -142,7 +140,7 @@ .desc { color: var(--grey); font-size: 14px; - margin-bottom: 1rem; + margin-bottom: 1.5rem; } .devmode { @@ -170,18 +168,67 @@ margin-top: 1rem; } -.chip { - border-radius: 4px; - height: 40px; -} - -.chip[color='green'] { - background-color: #56a392 !important; -} - .divider { flex-basis: 100%; margin: 1.5rem .5rem; height: 1px; background-color: rgba(#8795a1, .2); } + +.next-steps { + h5 { + text-transform: uppercase; + font-size: 14px; + color: var(--grey); + } + + .row { + width: 100%; + display: flex; + overflow-x: auto; + + .step { + min-width: 220px; + max-width: 280px; + padding: 1rem; + margin: 0 .5rem; + border: 1px solid var(--grey); + border-radius: .5rem; + display: flex; + flex-direction: column; + align-items: center; + box-sizing: border-box; + flex: 1; + + h6 { + font-size: 1rem; + text-align: center; + margin: 0 0 1rem 0; + } + + p { + font-size: 14px; + text-align: center; + color: var(--grey); + } + + .fill-space { + flex: 1; + } + + button { + display: block; + margin: auto; + } + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + } +} \ No newline at end of file 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 2e4cf4a1cb..4cc5c5ffda 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 @@ -4,12 +4,14 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { MatButtonToggleChange } from '@angular/material/button-toggle'; import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute, Params, Router } 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 { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component'; import { ChangeType } from 'src/app/modules/changes/changes.component'; +import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { Application, AppState, @@ -27,11 +29,7 @@ import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component'; - -enum RedirectType { - REDIRECT = 'redirect', - POSTREDIRECT = 'postredirect', -} +import { CODE_METHOD, getAuthMethodFromPartialConfig, getPartialConfigFromAuthMethod, IMPLICIT_METHOD, PKCE_METHOD, PK_JWT_METHOD, POST_METHOD, CUSTOM_METHOD } from '../authmethods'; @Component({ selector: 'app-app-detail', @@ -39,12 +37,20 @@ enum RedirectType { styleUrls: ['./app-detail.component.scss'], }) export class AppDetailComponent implements OnInit, OnDestroy { + public editState: boolean = false; + public currentAuthMethod: string = CUSTOM_METHOD.key; + public initialAuthMethod: string = CUSTOM_METHOD.key; public canWrite: boolean = false; public errorMessage: string = ''; public removable: boolean = true; public addOnBlur: boolean = true; public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; + public authMethods: RadioItemAuthType[] = [ + PKCE_METHOD, + CODE_METHOD, + POST_METHOD, + ]; private subscription?: Subscription; public projectId: string = ''; public app!: Application.AsObject; @@ -78,11 +84,10 @@ export class AppDetailComponent implements OnInit, OnDestroy { public AppState: any = AppState; public appNameForm!: FormGroup; public appForm!: FormGroup; + public redirectUrisList: string[] = []; public postLogoutRedirectUrisList: string[] = []; - public RedirectType: any = RedirectType; - public isZitadel: boolean = false; public docs!: ZitadelDocs.AsObject; @@ -90,9 +95,6 @@ export class AppDetailComponent implements OnInit, OnDestroy { public OIDCAuthMethodType: any = OIDCAuthMethodType; public OIDCTokenType: any = OIDCTokenType; - public redirectControl: FormControl = new FormControl({ value: '', disabled: true }); - public postRedirectControl: FormControl = new FormControl({ value: '', disabled: true }); - public ChangeType: any = ChangeType; constructor( public translate: TranslateService, @@ -103,6 +105,7 @@ export class AppDetailComponent implements OnInit, OnDestroy { private dialog: MatDialog, private mgmtService: ManagementService, private authService: GrpcAuthService, + private router: Router, ) { this.appNameForm = this.fb.group({ state: [{ value: '', disabled: true }, []], @@ -145,12 +148,23 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.mgmtService.GetApplicationById(projectid, id).then(app => { this.app = app.toObject(); this.appNameForm.patchValue(this.app); - console.log(this.app); + + this.getAuthMethodOptions(); + if (this.app.oidcConfig) { + this.initialAuthMethod = this.authMethodFromPartialConfig(this.app.oidcConfig); + this.currentAuthMethod = this.initialAuthMethod; + if (this.initialAuthMethod === CUSTOM_METHOD.key) { + if (!this.authMethods.includes(CUSTOM_METHOD)) { + this.authMethods.push(CUSTOM_METHOD); + } + } else { + this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD); + } + } + if (allowed) { this.appNameForm.enable(); this.appForm.enable(); - this.redirectControl.enable(); - this.postRedirectControl.enable(); } if (this.app.oidcConfig?.redirectUrisList) { @@ -161,12 +175,22 @@ export class AppDetailComponent implements OnInit, OnDestroy { } 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); } + + this.appForm.valueChanges.subscribe(oidcConfig => { + this.initialAuthMethod = this.authMethodFromPartialConfig(oidcConfig); + if (this.initialAuthMethod === CUSTOM_METHOD.key) { + if (!this.authMethods.includes(CUSTOM_METHOD)) { + this.authMethods.push(CUSTOM_METHOD); + } + } else { + this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD); + } + }); }).catch(error => { console.error(error); this.toast.showError(error); @@ -176,6 +200,69 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.docs = (await this.mgmtService.GetZitadelDocs()).toObject(); } + private getAuthMethodOptions(): void { + switch (this.app.oidcConfig?.applicationType) { + case OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE: + this.authMethods = [ + PKCE_METHOD, + CUSTOM_METHOD, + ]; + break; + case OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB: + this.authMethods = [ + PKCE_METHOD, + CODE_METHOD, + POST_METHOD, + ]; + break; + case OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT: + this.authMethods = [ + PKCE_METHOD, + IMPLICIT_METHOD, + ]; + break; + } + } + + public authMethodFromPartialConfig(config: OIDCConfig.AsObject): string { + const key = getAuthMethodFromPartialConfig(config); + return key; + } + + public setPartialConfigFromAuthMethod(authMethod: string): void { + const partialConfig = getPartialConfigFromAuthMethod(authMethod); + + if (partialConfig && this.app.oidcConfig) { + this.app.oidcConfig.responseTypesList = partialConfig.responseTypesList ?? []; + this.app.oidcConfig.grantTypesList = partialConfig.grantTypesList ?? []; + this.app.oidcConfig.authMethodType = partialConfig.authMethodType ?? OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE; + this.appForm.patchValue(this.app.oidcConfig); + } + } + + public deleteApp(): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'APP.PAGES.DIALOG.DELETE.TITLE', + descriptionKey: 'APP.PAGES.DIALOG.DELETE.DESCRIPTION', + }, + width: '400px', + }); + dialogRef.afterClosed().subscribe(resp => { + if (resp && this.projectId && this.app.id) { + this.mgmtService.RemoveApplication(this.projectId, this.app.id).then(() => { + this.toast.showInfo('APP.TOAST.DELETED', true); + + this.router.navigate(['/projects', this.projectId]); + }).catch(error => { + this.toast.showError(error); + }); + } + }); + } + public changeState(event: MatButtonToggleChange): void { if (event.value === AppState.APPSTATE_ACTIVE) { this.mgmtService.ReactivateApplication(this.projectId, this.app.id).then(() => { @@ -192,40 +279,6 @@ export class AppDetailComponent implements OnInit, OnDestroy { } } - public add(input: any, target: RedirectType): void { - if (target === RedirectType.POSTREDIRECT && this.postRedirectControl.valid) { - if (input.value !== '' && input.value !== ' ' && input.value !== '/') { - this.postLogoutRedirectUrisList.push(input.value); - } - if (input) { - input.value = ''; - } - } else if (target === RedirectType.REDIRECT && this.redirectControl.valid) { - if (input.value !== '' && input.value !== ' ' && input.value !== '/') { - this.redirectUrisList.push(input.value); - } - if (input) { - input.value = ''; - } - } - } - - public remove(redirect: any, target: RedirectType): void { - if (target === RedirectType.POSTREDIRECT) { - const index = this.postLogoutRedirectUrisList.indexOf(redirect); - - if (index >= 0) { - this.postLogoutRedirectUrisList.splice(index, 1); - } - } else if (target === RedirectType.REDIRECT) { - const index = this.redirectUrisList.indexOf(redirect); - - if (index >= 0) { - this.redirectUrisList.splice(index, 1); - } - } - } - public saveApp(): void { if (this.appNameForm.valid) { this.app.name = this.name?.value; @@ -234,6 +287,7 @@ export class AppDetailComponent implements OnInit, OnDestroy { .UpdateApplication(this.projectId, this.app.id, this.name?.value) .then(() => { this.toast.showInfo('APP.TOAST.OIDCUPDATED', true); + this.editState = false; }) .catch(error => { this.toast.showError(error); @@ -282,10 +336,12 @@ export class AppDetailComponent implements OnInit, OnDestroy { dur.setNanos((Math.floor(this.clockSkewSeconds?.value % 1) * 10000)); req.setClockSkew(dur); } - console.log(req.toObject()); this.mgmtService .UpdateOIDCAppConfig(req) .then(() => { + if (this.app.oidcConfig) { + this.currentAuthMethod = this.authMethodFromPartialConfig(this.app.oidcConfig); + } this.toast.showInfo('APP.TOAST.OIDCUPDATED', true); }) .catch(error => { diff --git a/console/src/app/pages/projects/apps/apps.module.ts b/console/src/app/pages/projects/apps/apps.module.ts index ff7fd7c0af..e1c1593c1c 100644 --- a/console/src/app/pages/projects/apps/apps.module.ts +++ b/console/src/app/pages/projects/apps/apps.module.ts @@ -19,6 +19,7 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; +import { AppRadioModule } from 'src/app/modules/app-radio/app-radio.module'; import { CardModule } from 'src/app/modules/card/card.module'; import { ChangesModule } from 'src/app/modules/changes/changes.module'; import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module'; @@ -29,15 +30,20 @@ import { AppCreateComponent } from './app-create/app-create.component'; import { AppDetailComponent } from './app-detail/app-detail.component'; import { AppSecretDialogComponent } from './app-secret-dialog/app-secret-dialog.component'; import { AppsRoutingModule } from './apps-routing.module'; +import { A11yModule } from '@angular/cdk/a11y'; +import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component'; @NgModule({ declarations: [ AppCreateComponent, AppDetailComponent, AppSecretDialogComponent, + RedirectUrisComponent, ], imports: [ CommonModule, + A11yModule, + AppRadioModule, AppsRoutingModule, FormsModule, TranslateModule, diff --git a/console/src/app/pages/projects/apps/authmethods.ts b/console/src/app/pages/projects/apps/authmethods.ts new file mode 100644 index 0000000000..6d16c4ca0d --- /dev/null +++ b/console/src/app/pages/projects/apps/authmethods.ts @@ -0,0 +1,166 @@ +import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component'; +import { OIDCAuthMethodType, OIDCConfig, OIDCGrantType, OIDCResponseType } from 'src/app/proto/generated/management_pb'; + +export const CODE_METHOD: RadioItemAuthType = { + key: 'CODE', + titleI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.CODE.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.CODE.DESCRIPTION', + disabled: false, + prefix: 'CODE', + background: 'rgb(89 115 128)', + responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, + grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, + authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, + recommended: false, +}; +export const PKCE_METHOD: RadioItemAuthType = { + key: 'PKCE', + titleI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.PKCE.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.PKCE.DESCRIPTION', + disabled: false, + prefix: 'PKCE', + background: 'rgb(80 110 92)', + responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, + grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, + authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, + recommended: true, +}; +export const POST_METHOD: RadioItemAuthType = { + key: 'POST', + titleI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.POST.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.POST.DESCRIPTION', + disabled: false, + prefix: 'POST', + background: '#595d80', + responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, + grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, + authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, + notRecommended: true, +}; +export const PK_JWT_METHOD: RadioItemAuthType = { + key: 'PK_JWT', + titleI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.ALTERNATIVE.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.ALTERNATIVE.DESCRIPTION', + disabled: false, + prefix: 'PK_JWT', + background: '#6a506e', + responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, + grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, + authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, +}; +export const IMPLICIT_METHOD: RadioItemAuthType = { + key: 'IMPLICIT', + titleI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.IMPLICIT.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.IMPLICIT.DESCRIPTION', + disabled: false, + prefix: 'IMP', + background: 'rgb(144 75 75)', + responseType: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, + grantType: OIDCGrantType.OIDCGRANTTYPE_IMPLICIT, + authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, + notRecommended: true, +}; +export const CUSTOM_METHOD: RadioItemAuthType = { + key: 'CUSTOM', + titleI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.CUSTOM.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.AUTHMETHOD.CUSTOM.DESCRIPTION', + disabled: false, + prefix: 'CUSTOM', + background: '#333', +}; + +export function getPartialConfigFromAuthMethod(authMethod: string): Partial | undefined { + let config: Partial; + switch (authMethod) { + case CODE_METHOD.key: + config = { + responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], + grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, + }; + return config; + case PKCE_METHOD.key: + config = { + responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], + grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, + }; + return config; + case POST_METHOD.key: + config = { + responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], + grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, + }; + return config; + // case PK_JWT_METHOD.key: + // config = { + // responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], + // grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + // authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, + // }; + // return config; + case IMPLICIT_METHOD.key: + config = { + responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN], + grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_IMPLICIT], + authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, + }; + return config; + default: + return undefined; + } +} + +export function getAuthMethodFromPartialConfig(config: Partial | OIDCConfig.AsObject): string { + const toCheck = [config.responseTypesList, config.grantTypesList, config.authMethodType]; + const code = JSON.stringify( + [ + [OIDCResponseType.OIDCRESPONSETYPE_CODE], + [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, + ] + ); + + const pkce = JSON.stringify( + [ + [OIDCResponseType.OIDCRESPONSETYPE_CODE], + [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, + ] + ); + + const post = JSON.stringify( + [ + [OIDCResponseType.OIDCRESPONSETYPE_CODE], + [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, + ] + ); + + // const pk_jwt = JSON.stringify( + // [ + // [OIDCResponseType.OIDCRESPONSETYPE_CODE], + // [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], + // OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, + // ] + // ); + + const implicit = JSON.stringify( + [ + [OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN], + [OIDCGrantType.OIDCGRANTTYPE_IMPLICIT], + OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, + ] + ); + + switch (JSON.stringify(toCheck)) { + case code: return CODE_METHOD.key; + case pkce: return PKCE_METHOD.key; + case post: return POST_METHOD.key; + // case pk_jwt: return PK_JWT_METHOD.key; + case implicit: return IMPLICIT_METHOD.key; + default: + return CUSTOM_METHOD.key; + } +} \ No newline at end of file diff --git a/console/src/app/pages/projects/apps/authtypes.ts b/console/src/app/pages/projects/apps/authtypes.ts new file mode 100644 index 0000000000..9e352459e3 --- /dev/null +++ b/console/src/app/pages/projects/apps/authtypes.ts @@ -0,0 +1,25 @@ +import { OIDCApplicationType } from 'src/app/proto/generated/management_pb'; + +export const WEB_TYPE = { + titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.DESCRIPTION', + type: OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB, + prefix: 'WEB', + background: 'rgb(80, 110, 110)', +}; + +export const USER_AGENT_TYPE = { + titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.DESCRIPTION', + type: OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT, + prefix: 'UA', + background: '#6a506e', +}; + +export const NATIVE_TYPE = { + titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.TITLE', + descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.DESCRIPTION', + type: OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE, + prefix: 'N', + background: '#595d80', +}; diff --git a/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.html b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.html new file mode 100644 index 0000000000..036853c5ff --- /dev/null +++ b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.html @@ -0,0 +1,28 @@ +
+ + {{ title }} + + + + + + +
+
+ {{uri}} + + + + + +
+
+ +

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

\ No newline at end of file diff --git a/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.scss b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.scss new file mode 100644 index 0000000000..9645db0a65 --- /dev/null +++ b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.scss @@ -0,0 +1,61 @@ +.form { + display: flex; + align-items: flex-end; + min-width: 320px; + + .formfield { + flex: 1; + } + + button { + margin-bottom: 14px; + margin-right: -0.5rem; + } +} +.uri-list { + margin: 0 .5rem; + width: 100%; + + .uri-line { + display: flex; + align-items: center; + + .uri { + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 14px; + } + + .fill-space { + flex: 1; + } + + i.green { + font-size: 1rem; + line-height: 35px; + height: 30px; + } + + i.red { + font-size: 1.2rem; + } + + .icon-button { + height: 30px; + line-height: 30px; + + mat-icon { + font-size: 1rem !important; + } + + &:not(:hover) { + color: var(--grey); + } + } + } +} + +.error { + font-size: 13px; + color: #f44336; + margin: 0 .5rem 1.5rem .5rem; +} \ No newline at end of file diff --git a/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.spec.ts b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.spec.ts new file mode 100644 index 0000000000..a09b84deb9 --- /dev/null +++ b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RedirectUrisComponent } from './redirect-uris.component'; + +describe('RedirectUrisComponent', () => { + let component: RedirectUrisComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RedirectUrisComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RedirectUrisComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.ts b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.ts new file mode 100644 index 0000000000..3cf7083e5e --- /dev/null +++ b/console/src/app/pages/projects/apps/redirect-uris/redirect-uris.component.ts @@ -0,0 +1,43 @@ +import { Component, EventEmitter, Input, OnInit } from '@angular/core'; +import { FormControl } from '@angular/forms'; + +@Component({ + selector: 'cnsl-redirect-uris', + templateUrl: './redirect-uris.component.html', + styleUrls: ['./redirect-uris.component.scss'] +}) +export class RedirectUrisComponent implements OnInit { + @Input() title: string = ''; + @Input() devMode: boolean = false; + @Input() canWrite: boolean = false; + @Input() public urisList: string[] = []; + @Input() public redirectControl: FormControl = new FormControl({ value: '', disabled: true }); + @Input() public changedUris: EventEmitter = new EventEmitter(); + constructor() { } + + ngOnInit(): void { + if (this.canWrite) { + this.redirectControl.enable(); + } + } + + public add(input: any): void { + if (this.redirectControl.valid) { + if (input.value !== '' && input.value !== ' ' && input.value !== '/') { + this.urisList.push(input.value); + } + if (input) { + input.value = ''; + } + } + } + + public remove(redirect: any): void { + console.log(redirect); + const index = this.urisList.indexOf(redirect); + + if (index >= 0) { + this.urisList.splice(index, 1); + } + } +} diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.html index b34e25bdaa..2d0ab1c563 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.html @@ -11,18 +11,27 @@
-
+ *ngFor="let app of appsSubject | async" + matTooltip="{{'APP.OIDC.APPTYPE'+app.oidcConfig.applicationType | translate}}"> + {{ app.name.charAt(0)}} -
+ + + + {{app.name}} + {{'APP.OIDC.APPTYPE'+app.oidcConfig.applicationType | translate}} +
-
+ add -
+ {{ 'ACTIONS.NEW' | translate }}
diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.scss b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.scss index 4781ae077b..14c5803970 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.scss +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.scss @@ -1,82 +1,62 @@ -@import '~@angular/material/theming'; -@mixin application-grid-theme($theme) { - /* stylelint-disable */ - $primary: map-get($theme, primary); - $primary-dark: mat-color($primary, A900); - $accent: map-get($theme, accent); - $accent-color: mat-color($primary, 500); - /* stylelint-enable */ - .app-grid-header { +.app-grid-header { display: flex; align-items: center; .fill-space { - flex: 1; + flex: 1; } - } +} - .app-container { +.app-container { display: flex; flex-wrap: wrap; margin: 0 -1rem; padding-bottom: 2rem; .sp-container { - display: flex; - justify-content: center; - align-items: center; - width: calc(82px + 2rem); - height: calc(82px + 2rem); - } - - .app-wrap { - outline: none; - display: flex; - flex-direction: column; - align-items: center; - max-width: 150px; - - .morph-card { - cursor: pointer; - animation: all .2s; display: flex; justify-content: center; align-items: center; - font-size: 2rem; - height: 80px; - width: 80px; - margin: 1rem; - text-transform: uppercase; - border-radius: .5rem; - border: 2px solid $accent-color; - font-weight: 800; - background-color: $primary-dark; - transition: background-color box-shadow .3s ease-in; - background-image: - linear-gradient(transparent 11px, rgba($accent-color, .5) 12px, transparent 12px), - linear-gradient(90deg, transparent 11px, rgba($accent-color, .5) 12px, transparent 12px); - background-size: 100% 12px, 12px 100%; - - &:hover { - background-color: rgba($accent-color, .1); - } - - &.add { - background: $accent-color; - color: white; - - &:hover { - background-color: rgba($accent-color, .8); - } - } - } - - .name { - font-size: .8rem; - color: #8a868a; - } + width: calc(82px + 2rem); + height: calc(82px + 2rem); + } + + .app-wrap { + outline: none; + display: flex; + flex-direction: column; + align-items: center; + max-width: 150px; + + .grid-card { + cursor: pointer; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + font-size: 2rem; + height: 80px; + width: 80px; + margin: 1rem; + text-transform: uppercase; + border-radius: .5rem; + font-weight: 800; + box-sizing: border-box; + + &.add { + border: 2px solid var(--grey); + } + } + + .name { + font-size: 14px; + } + + .type { + font-size: 12px; + color: #8a868a; + } } - } } diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts index e0e26b06c2..a73e8687c2 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts @@ -1,8 +1,9 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { catchError, finalize, map } from 'rxjs/operators'; -import { Application } from 'src/app/proto/generated/management_pb'; +import { Application, OIDCApplicationType, OIDCResponseType } from 'src/app/proto/generated/management_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; +import { NATIVE_TYPE, USER_AGENT_TYPE, WEB_TYPE } from '../../../apps/authtypes'; @Component({ selector: 'app-application-grid', @@ -16,6 +17,11 @@ export class ApplicationGridComponent implements OnInit { public appsSubject: BehaviorSubject = new BehaviorSubject([]); private loadingSubject: BehaviorSubject = new BehaviorSubject(true); public loading$: Observable = this.loadingSubject.asObservable(); + public OIDCApplicationType: any = OIDCApplicationType; + + public NATIVE_TYPE: any = NATIVE_TYPE; + public WEB_TYPE: any = WEB_TYPE; + public USER_AGENT_TYPE: any = USER_AGENT_TYPE; constructor(private mgmtService: ManagementService) { } diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.html index 9012cba3ad..21445b24ee 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.html @@ -24,10 +24,17 @@ -
- + + + + + + + diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.ts index bbfb36ef08..c4b70f1431 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/applications/applications.component.ts @@ -26,7 +26,7 @@ export class ApplicationsComponent implements AfterViewInit, OnInit { public dataSource!: ProjectApplicationsDataSource; public selection: SelectionModel = new SelectionModel(true, []); - public displayedColumns: string[] = ['select', 'name']; + public displayedColumns: string[] = ['select', 'name', 'type']; constructor(private mgmtService: ManagementService, private toast: ToastService) { } diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html index 5a336524aa..d83a9208e7 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html @@ -8,7 +8,7 @@ @@ -24,11 +24,13 @@ + (click)="changeState(ProjectState.PROJECTSTATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' | + translate}} + (click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' | + translate}}
@@ -78,7 +80,7 @@ -

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

{{'PROJECT.STATE.TITLE' | translate}}: {{'PROJECT.STATE.'+project.state | translate}} + [ngClass]="{'active': project.state === ProjectState.PROJECTSTATE_ACTIVE, 'inactive': project.state === ProjectState.PROJECTSTATE_INACTIVE}">{{'PROJECT.STATE.'+project.state + | translate}}
diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts index 269742928c..04d52105a3 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.module.ts @@ -14,6 +14,7 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module'; +import { AppCardModule } from 'src/app/modules/app-card/app-card.module'; import { CardModule } from 'src/app/modules/card/card.module'; import { ChangesModule } from 'src/app/modules/changes/changes.module'; import { ContributorsModule } from 'src/app/modules/contributors/contributors.module'; @@ -43,6 +44,7 @@ import { ProjectGrantsComponent } from './project-grants/project-grants.componen imports: [ CommonModule, FormsModule, + AppCardModule, OwnedProjectDetailRoutingModule, TranslateModule, ReactiveFormsModule, diff --git a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html index 6e72151365..031c088860 100644 --- a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html +++ b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.html @@ -39,7 +39,7 @@

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

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

- + \ No newline at end of file diff --git a/console/src/app/pages/signedout/signedout.component.scss b/console/src/app/pages/signedout/signedout.component.scss index 1577de9c7f..d5320c36a7 100644 --- a/console/src/app/pages/signedout/signedout.component.scss +++ b/console/src/app/pages/signedout/signedout.component.scss @@ -17,19 +17,24 @@ p { color: var(--grey); text-align: center; - font-size: 1rem; + font-size: 14px; margin: 0; margin-bottom: 3rem; } img { height: 100px; + max-width: 170px; margin-bottom: 2rem; } button { display: block; padding: .5rem 4rem; + + i { + margin-left: .5rem; + } } } } 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 e28affdae5..9131ff01a8 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 @@ -77,7 +77,7 @@ - + diff --git a/console/src/app/pages/users/user-detail/memberships/memberships.component.ts b/console/src/app/pages/users/user-detail/memberships/memberships.component.ts index 68b3d7ece6..c4b2610999 100644 --- a/console/src/app/pages/users/user-detail/memberships/memberships.component.ts +++ b/console/src/app/pages/users/user-detail/memberships/memberships.component.ts @@ -3,8 +3,10 @@ import { Component, Input, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; +import { AuthServiceClient } from 'src/app/proto/generated/auth_grpc_web_pb'; import { MemberType, UserMembershipSearchResponse, UserView } from 'src/app/proto/generated/management_pb'; import { AdminService } from 'src/app/services/admin.service'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; @@ -33,12 +35,14 @@ export class MembershipsComponent implements OnInit { public loading: boolean = false; public memberships!: UserMembershipSearchResponse.AsObject; + @Input() public auth: boolean = false; @Input() public user!: UserView.AsObject; @Input() public disabled: boolean = false; public MemberType: any = MemberType; constructor( + private authService: GrpcAuthService, private mgmtService: ManagementService, private adminService: AdminService, private dialog: MatDialog, @@ -51,10 +55,17 @@ export class MembershipsComponent implements OnInit { } public async loadManager(userId: string): Promise { - this.mgmtService.SearchUserMemberships(userId, 100, 0, []).then(response => { - this.memberships = response.toObject(); - this.loading = false; - }); + if (this.auth) { + this.authService.SearchUserMemberships(100, 0, []).then(response => { + this.memberships = response.toObject(); + this.loading = false; + }); + } else { + this.mgmtService.SearchUserMemberships(userId, 100, 0, []).then(response => { + this.memberships = response.toObject(); + this.loading = false; + }); + } } public navigateToObject(): void { diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss index 0f113e67c5..154ae46fb8 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss @@ -1,7 +1,6 @@ .head { display: flex; align-items: center; - border-bottom: 1px solid #ffffff20; flex-wrap: wrap; padding-bottom: .5rem; diff --git a/console/src/app/services/grpc-auth.service.ts b/console/src/app/services/grpc-auth.service.ts index 0022c24126..915cdcdfb7 100644 --- a/console/src/app/services/grpc-auth.service.ts +++ b/console/src/app/services/grpc-auth.service.ts @@ -26,6 +26,9 @@ import { UpdateUserProfileRequest, UserAddress, UserEmail, + UserMembershipSearchQuery, + UserMembershipSearchRequest, + UserMembershipSearchResponse, UserPhone, UserProfile, UserProfileView, @@ -241,6 +244,16 @@ export class GrpcAuthService { ); } + public SearchUserMemberships(limit: number, offset: number, queryList?: UserMembershipSearchQuery[]): Promise { + const req = new UserMembershipSearchRequest(); + req.setLimit(limit); + req.setOffset(offset); + if (queryList) { + req.setQueriesList(queryList); + } + return this.grpcService.auth.searchMyUserMemberships(req); + } + public GetMyUserEmail(): Promise { return this.grpcService.auth.getMyUserEmail( new Empty(), diff --git a/console/src/app/services/interceptors/auth.interceptor.ts b/console/src/app/services/interceptors/auth.interceptor.ts index 37b1371dc0..08e13e0ab0 100644 --- a/console/src/app/services/interceptors/auth.interceptor.ts +++ b/console/src/app/services/interceptors/auth.interceptor.ts @@ -55,6 +55,7 @@ export class AuthInterceptor implements UnaryIn titleKey: 'ERRORS.TOKENINVALID.TITLE', descriptionKey: 'ERRORS.TOKENINVALID.DESCRIPTION', }, + disableClose: true, width: '400px', }); diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index f76b8f5cc8..0af1152969 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -1362,4 +1362,11 @@ export class ManagementService { public UpdateOIDCAppConfig(req: OIDCConfigUpdate): Promise { return this.grpcService.mgmt.updateApplicationOIDCConfig(req); } + + public RemoveApplication(projectId: string, appId: string): Promise { + const req = new ApplicationID(); + req.setId(appId); + req.setProjectId(projectId); + return this.grpcService.mgmt.removeApplication(req); + } } diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 7f12d7b93f..c6b055be79 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1,5 +1,29 @@ { "APP_NAME": "ZITADEL", + "ONBOARDING": { + "HEADER":"Erste Schritte", + "TITLE":"Lernen Sie unsere ZITADEL Console kennen.", + "LOGINDESC":"Melden Sie sich an um Zugriff auf Funktionen von Zitadel zu erhalten", + "LOGIN":"Anmelden", + "DESCRIPTION":"Führen Sie die folgenden Schritte aus und passen Sie ZITADEL optimal an Ihre Bedürfnisse an.", + "STEPS_TITLE":"Führen Sie die folgenden Schritte in gewünschter Reihenfolge aus:", + "STEPS": { + "1": { + "TITLE":"Projekt erstellen", + "DESC":"Erstellen Sie Ihr erstes Projekt und legen Sie Zugangsberechtigungen eventueller Mitarbeiter fest." + }, + "2": { + "TITLE":"Applikation erstellen", + "DESC":"Erstellen Sie eine Applikation innerhalb Ihres Projektes und legen Sie dessen Eigenschaften fest." + }, + "3": { + "TITLE":"Sicherheit verbessern", + "DESC":"Erweitern Sie die Zugangsrichtlinien und erhöhen Sie dadurch die Sicherheit ihrer Benutzer." + } + }, + "START":"Start", + "DOCS":"Docs" + }, "HOME": { "TITLE": "ZITADEL", "SECURITYANDPRIVACY": "Datenschutz und Personalisierung", @@ -32,9 +56,15 @@ "WELCOME":"Willkommen", "WELCOMESENTENCE":"Hier findest Du die empfohlenen Aktionen basierend auf Deinen zuletzt erworbenen Berechtigungen. Beachte bitte, dass Du möglicherweise Deine Organisation in der Kopfzeile wechseln musst.", "DISCLAIMER":"Du kannst nur die Einstellungen Deiner aktuellen Organisation, die in der Kopfzeile angegeben ist, sehen. ZITADEL behandelt Deine Daten vertraulich und sicher.", - "DISCLAIMERLINK":"Mehr Informationen zur Sicherheit" + "DISCLAIMERLINK":"Mehr Informationen zur Sicherheit", + "QUICKSTARTS": { + "LABEL":"Erste Schritte", + "TITLE":"Quickstarts", + "DESCRIPTION":"Mit ZITADEL schnell durchstarten." + } }, "MENU": { + "DASHBOARD":"Übersicht", "PERSONAL_INFO": "Persönliche Informationen", "DOCUMENTATION":"Dokumentation", "IAMPOLICIES":"IAM", @@ -54,6 +84,7 @@ "SHOWORGS":"Alle Organisationen anzeigen", "GRANTSECTION":"Berechtigungssektion", "GRANTS":"Berechtigungen", + "TOC":"Datenschutz und AGB", "TOOLTIP": { "PERSONAL":"Verwalte deinen persönlichen Account, deine IDPs, Login Methoden, Faktoren und Berechtigungen", "IAMPOLICIES":"Verwalte ZITADELs globale Zugangsrichtlinien und verwalte ZITADEL Manager", @@ -930,12 +961,34 @@ "1": "Aktiv", "2": "Inaktiv" } + }, + "DIALOG": { + "DELETE": { + "TITLE": "App löschen", + "DESCRIPTION":"Wollen Sie diese App wirklich löschen?" + } + }, + "NEXTSTEPS": { + "TITLE":"Nächste Schritte", + "0": { + "TITLE":"Rollen festlegen", + "DESC":"Erfassen Sie Rollen für ihr Projekt" + }, + "1": { + "TITLE":"Benutzer hinzufügen", + "DESC":"Fügen Sie Nutzer ihrer Organisation hinzu" + }, + "2": { + "TITLE":"Hilfe & Support", + "DESC":"Lesen Sie unsere Dokumentation zum Erstellen von Applikation oder kontaktieren Sie unseren Support" + } } }, "NAME": "Name", "TYPE":"Anwendungstyp", "GRANT":"Berechtigungstypen", "OIDC": { + "CURRENT":"Aktuelle Konfiguration", "TOKENSECTIONTITLE":"AuthToken Optionen", "REDIRECTSECTIONTITLE":"Weiterleitungseinstellungen", "PROSWITCH":"Konfigurator überspringen", @@ -991,13 +1044,58 @@ "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." + "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.", + "RECOMMENDED":"Empfohlen", + "NOTRECOMMENDED":"nicht empfohlen", + "SELECTION":{ + "APPTYPE": { + "WEB": { + "TITLE":"Web", + "DESCRIPTION":"Standard Web applications wie .net, PHP, Node.js, Java, etc." + }, + "NATIVE": { + "TITLE":"NATIVE", + "DESCRIPTION":"Mobile Apps, Desktop, Smart Devices, etc." + }, + "USERAGENT": { + "TITLE":"User Agent", + "DESCRIPTION":"Single Page Applications (SPA) und grundsätzlich alle im Browser aufgeführten JS Frameworks" + } + }, + "AUTHMETHOD": { + "CODE": { + "TITLE":"Code", + "DESCRIPTION":"Tausche den Authorization Code gegen Tokens ein" + }, + "PKCE": { + "TITLE":"PKCE", + "DESCRIPTION":"Nutze einen Zufalls Hash Wert anstelle des Client Secret für mehr Sicherheit" + }, + "POST": { + "TITLE":"POST", + "DESCRIPTION":"Sende client_id und client_secret im (HTML) Formular" + }, + "PK_JWT": { + "TITLE":"Private Key JWT", + "DESCRIPTION":"Nutze einen Private Key um deine Application zu authentifizieren" + }, + "IMPLICIT": { + "TITLE":"Implicit", + "DESCRIPTION":"Erhalte die Token direkt vom authorize Endpoint" + }, + "CUSTOM": { + "TITLE":"Custom", + "DESCRIPTION":"Deine Konfiguration entspricht keiner anderen Option." + } + } + } }, "TOAST": { "REACTIVATED":"Anwendung reaktiviert.", "DEACTIVATED":"Anwendung deaktiviert.", "OIDCUPDATED":"OIDC-Konfiguration geändert.", - "OIDCCLIENTSECRETREGENERATED":"OIDC-Client Secret generiert." + "OIDCCLIENTSECRETREGENERATED":"OIDC-Client Secret generiert.", + "DELETED":"App gelöscht." } }, "GENDERS": { diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index ee90a3b6c8..36f606c5fb 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1,5 +1,29 @@ { "APP_NAME": "ZITADEL", + "ONBOARDING": { + "HEADER":"First Steps", + "TITLE":"Learn how to use ZITADEL", + "LOGINDESC":"You need to log in to control your ZITADEL settings.", + "LOGIN":"Login", + "DESCRIPTION":"We at ZITADEL care a lot about security and performance. Carry out the following steps and adapt ZITADEL optimally to your needs.", + "STEPS_TITLE":"Complete the following steps in the order you want:", + "STEPS": { + "1": { + "TITLE":"Create a project", + "DESC":"Create your first project and define access rights for any employees." + }, + "2": { + "TITLE":"Create Application", + "DESC":"Create an application within your project and define its properties." + }, + "3": { + "TITLE":"Improve security", + "DESC":"Extend the access policies and thereby increase the security of your users." + } + }, + "START":"Start", + "DOCS":"Docs" + }, "HOME": { "TITLE": "ZITADEL", "SECURITYANDPRIVACY": "Data Protection and Personalisation", @@ -32,9 +56,15 @@ "WELCOME":"Welcome", "WELCOMESENTENCE":"Here you can find recommended actions based on your last acquired permissions. Note that you may have to select your organisation in the header above.", "DISCLAIMER":"You can only see settings of your current organisation specified in the header. ZITADEL treats your data confidentially and securely.", - "DISCLAIMERLINK":"Further information" + "DISCLAIMERLINK":"Further information", + "QUICKSTARTS": { + "LABEL":"First Steps", + "TITLE":"Quickstarts", + "DESCRIPTION":"Get started with ZITADEL quickly." + } }, "MENU": { + "DASHBOARD":"Overview", "PERSONAL_INFO": "Personal Information", "DOCUMENTATION":"Documentation", "IAMPOLICIES":"IAM", @@ -54,6 +84,7 @@ "SHOWORGS":"Show All Organisations", "GRANTSECTION":"Authorization Section", "GRANTS":"Authorizations", + "TOC":"Privacy Policy and TOC", "TOOLTIP": { "PERSONAL":"Show your Personal Account, your IDPs, Login methods, Factors and Authorisations", "IAMPOLICIES":"Manage ZITADELs global Access policies und elect ZITADEL Managers", @@ -930,12 +961,34 @@ "1": "Active", "2": "Inactive" } + }, + "DIALOG": { + "DELETE": { + "TITLE": "Delete App", + "DESCRIPTION":"Do you really want to delete this application?" + } + }, + "NEXTSTEPS": { + "TITLE":"Next Steps", + "0": { + "TITLE":"Add roles", + "DESC":"Enter your project roles" + }, + "1": { + "TITLE":"Add users", + "DESC":"Add new users of your organization" + }, + "2": { + "TITLE":"Help & Support", + "DESC":"Read our documentation on creating applications or contact our support" + } } }, "NAME": "Name", "TYPE":"Application Type", "GRANT":"Grant Types", "OIDC": { + "CURRENT":"Current Config", "TOKENSECTIONTITLE":"AuthToken Options", "REDIRECTSECTIONTITLE":"Redirect Settings", "PROSWITCH":"I'm a pro. Skip this wizard.", @@ -991,13 +1044,58 @@ "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." + "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.", + "RECOMMENDED":"recommended", + "NOTRECOMMENDED":"not recommended", + "SELECTION":{ + "APPTYPE": { + "WEB": { + "TITLE":"Web", + "DESCRIPTION":"Regular Web applications like .net, PHP, Node.js, Java, etc." + }, + "NATIVE": { + "TITLE":"NATIVE", + "DESCRIPTION":"Mobile Apps, Desktop, Smart Devices, etc." + }, + "USERAGENT": { + "TITLE":"User Agent", + "DESCRIPTION":"Single Page Applications (SPA) and in general all JS frameworks executed in browsers" + } + }, + "AUTHMETHOD": { + "CODE": { + "TITLE":"Code", + "DESCRIPTION":"Exchange the authorization code for the tokens" + }, + "PKCE": { + "TITLE":"PKCE", + "DESCRIPTION":"Use a random hash instead of a static client secret for more security" + }, + "POST": { + "TITLE":"POST", + "DESCRIPTION":"Send client_id and client_secret as part of the form" + }, + "PK_JWT": { + "TITLE":"Private Key JWT", + "DESCRIPTION":"Use a private key to authorize your application" + }, + "IMPLICIT": { + "TITLE":"Implicit", + "DESCRIPTION":"Get the tokens directly from the authorization endpoint" + }, + "CUSTOM": { + "TITLE":"Custom", + "DESCRIPTION":"Your setting doesn't correspond to any other option." + } + } + } }, "TOAST": { "REACTIVATED":"Application reactivated.", "DEACTIVATED":"Application deactivated.", "OIDCUPDATED":"OIDC configuration updated.", - "OIDCCLIENTSECRETREGENERATED":"OIDC client secret generated." + "OIDCCLIENTSECRETREGENERATED":"OIDC client secret generated.", + "DELETED":"App deleted." } }, "GENDERS": { diff --git a/console/src/assets/images/zitadel-logo-solo-dark.svg b/console/src/assets/images/zitadel-logo-solo-dark.svg new file mode 100644 index 0000000000..4d3181174e --- /dev/null +++ b/console/src/assets/images/zitadel-logo-solo-dark.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/console/src/assets/images/zitadel-logo-solo-light.svg b/console/src/assets/images/zitadel-logo-solo-light.svg new file mode 100644 index 0000000000..df44ec5398 --- /dev/null +++ b/console/src/assets/images/zitadel-logo-solo-light.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index 40f16d4354..1d814219a8 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -5,24 +5,29 @@ @import './styles/link.scss'; @import './styles/sidenav-list'; @import 'src/app/modules/avatar/avatar.component'; +@import 'src/app/modules/app-radio/app-type-radio/app-type-radio.component'; +@import 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component'; @import 'src/app/modules/changes/changes.component'; @import 'src/app/modules/info-section/info-section.component'; @import 'src/app/modules/detail-layout/detail-layout.component'; -@import 'src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component'; +@import 'src/app/modules/app-card/app-card.component'; @import 'src/app/pages/users/user-detail/auth-user-detail/theme-setting/theme-card'; @import 'src/app/pages/users/user-detail/memberships/memberships.component'; @import 'src/app/app.component.scss'; @import 'src/app/modules/form-field/form-field.component.scss'; @import 'src/app/modules/label/label.component.scss'; @import 'src/app/modules/meta-layout/meta.scss'; +@import 'src/app/modules/onboarding/onboarding.component.scss'; @mixin component-themes($theme) { @include avatar-theme($theme); + @include app-type-radio-theme($theme); + @include app-auth-method-radio-theme($theme); @include card-theme($theme); @include table-theme($theme); @include detail-layout-theme($theme); @include sidenav-list-theme($theme); - @include application-grid-theme($theme); + @include app-card-theme($theme); @include membership-theme($theme); @include changes-theme($theme); @include theme-card($theme); @@ -34,4 +39,5 @@ @include link-theme($theme); @include meta-theme($theme); @include info-section-theme($theme); + @include onboarding-theme($theme); } diff --git a/console/src/styles.scss b/console/src/styles.scss index 3eaff83014..fbe3b6a0c9 100644 --- a/console/src/styles.scss +++ b/console/src/styles.scss @@ -219,18 +219,21 @@ $custom-typography: box-shadow: inset 0 0 6px rgba(0, 0, 0, .3); background-color: #2d2e30; border-radius: 8px; + transition: all .3s cubic-bezier(.645, .045, .355, 1) !important; } ::-webkit-scrollbar { width: 6px; height: 6px; background-color: #2d2e30; + transition: all .3s cubic-bezier(.645, .045, .355, 1) !important; } ::-webkit-scrollbar-thumb { background-color: #737c8870; border-radius: 8px; cursor: pointer; + transition: all .3s cubic-bezier(.645, .045, .355, 1) !important; } .root-header { diff --git a/console/src/styles/sidenav-list.scss b/console/src/styles/sidenav-list.scss index baf779d774..fdd8600b8f 100644 --- a/console/src/styles/sidenav-list.scss +++ b/console/src/styles/sidenav-list.scss @@ -16,18 +16,23 @@ box-shadow: inset 1px 0 if($is-dark-theme, #303131, #e3e8ee); } + .sidenav { + box-shadow: inset -1px 0 if($is-dark-theme, #303131, #e3e8ee); + transition: all .3s cubic-bezier(.645, .045, .355, 1) !important; + } + .nav-item { color: mat-color($foreground, text) !important; &:hover { - background-color: $sec-dark; + background-color: if($is-dark-theme, $sec-dark, rgb(84 105 212 / 6%)); border-top-right-radius: 1.5rem; border-bottom-right-radius: 1.5rem; } &.active { color: $primary-color !important; - background-color: rgba($color: $primary-color, $alpha: .1) !important; + background-color: if($is-dark-theme, rgba($color: $primary-color, $alpha: .1), rgb(84 105 212 / 6%)) !important; } .c_label { @@ -54,7 +59,16 @@ .root-header { box-shadow: inset 0 -1px #e3e8ee; background-color: $primary-dark !important; - transition: background-color .3s cubic-bezier(.645, .045, .355, 1); + transition: all .3s cubic-bezier(.645, .045, .355, 1); + + .slash { + color: if($is-dark-theme, #525454, #d1d5d9); + } + + .org-button { + border: 1px solid if($is-dark-theme, #303131, #e3e8ee); + transition: all .3s cubic-bezier(.645, .045, .355, 1) !important; + } } .admin-line { @@ -63,7 +77,7 @@ align-items: center; bottom: 0; left: 0; - right: calc(100vw - 300px); + right: calc(100vw - 280px); background-color: $primary-color; color: white; z-index: 1; diff --git a/console/src/styles/table.scss b/console/src/styles/table.scss index a935730508..149a4cb5a7 100644 --- a/console/src/styles/table.scss +++ b/console/src/styles/table.scss @@ -64,7 +64,8 @@ } } - .mat-checkbox { + td .mat-checkbox, + th .mat-checkbox { margin-left: 1rem; } diff --git a/go.mod b/go.mod index f1a3c5e377..baa42a0054 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ 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.2 + github.com/caos/oidc v0.14.0 github.com/caos/orbos v1.5.14-0.20210205131708-6dc812182dc0 github.com/cockroachdb/cockroach-go/v2 v2.1.0 github.com/duo-labs/webauthn v0.0.0-20200714211715-1daaee874e43 @@ -56,7 +56,7 @@ require ( github.com/rs/cors v1.7.0 github.com/sony/sonyflake v1.0.0 github.com/spf13/cobra v0.0.7 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect github.com/ttacon/libphonenumber v1.1.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.13.0 @@ -69,7 +69,7 @@ require ( golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 // indirect golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect - golang.org/x/text v0.3.4 + golang.org/x/text v0.3.5 golang.org/x/tools v0.0.0-20201103235415-b653051172e4 google.golang.org/api v0.34.0 google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 717321d282..0e642d25f0 100644 --- a/go.sum +++ b/go.sum @@ -108,6 +108,7 @@ github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKS github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -116,6 +117,7 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= @@ -124,6 +126,7 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -137,12 +140,8 @@ github.com/caos/logging v0.0.0-20191210002624-b3260f690a6a/go.mod h1:9LKiDE2ChuG 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/oidc v0.6.2/go.mod h1:ozoi3b+aY33gzdvjz4w90VZShIHGsmDa0goruuV0arQ= -github.com/caos/oidc v0.13.2 h1:52oP3KB1UrZuwraBTLuwM9ItRIhJQMYOm1J5uQ0sYXw= -github.com/caos/oidc v0.13.2/go.mod h1:dLvfYUiAt9ORfl77L/KkcWuR/N0ll8Ry1nD2ERsamDY= -github.com/caos/orbos v1.5.14-0.20210128140136-842933949472 h1:iti4tAKxBknjJkQcDKWaxlj9Jbng5kz5TpQzzyda49o= -github.com/caos/orbos v1.5.14-0.20210128140136-842933949472/go.mod h1:ZLxNgPuYIlSvr80trezGGUIXng9gY2hHEdky/m0B/P0= -github.com/caos/orbos v1.5.14-0.20210202122121-ad32524ffc73 h1:usYmCT11HvwxBCk1+DSCmEU6CVYhzY8jHaQHSJMrxlg= -github.com/caos/orbos v1.5.14-0.20210202122121-ad32524ffc73/go.mod h1:ZLxNgPuYIlSvr80trezGGUIXng9gY2hHEdky/m0B/P0= +github.com/caos/oidc v0.14.0 h1:l7mTqYDpqNRZF9Vwzq5KAQd1wQCThdceL5HpsEMGoao= +github.com/caos/oidc v0.14.0/go.mod h1:CPsubVrA110OyLnCKwVZjTdsAVwq67DTbYIvux7UgbY= github.com/caos/orbos v1.5.14-0.20210205131708-6dc812182dc0 h1:N+KYBwuQO3QPr/nTUaNwjAetjp3NU4MP8Nv9Iue53UE= github.com/caos/orbos v1.5.14-0.20210205131708-6dc812182dc0/go.mod h1:ZLxNgPuYIlSvr80trezGGUIXng9gY2hHEdky/m0B/P0= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -154,9 +153,11 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -186,13 +187,17 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -212,6 +217,7 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -224,23 +230,28 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m 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/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ= github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -298,6 +309,7 @@ github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-sql-driver/mysql v1.4.0/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-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -308,6 +320,7 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -367,8 +380,10 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -383,6 +398,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -390,6 +407,7 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/googleinterns/cloud-operations-api-mock v0.0.0-20200709193332-a1e58c29bdd3 h1:eHv/jVY/JNop1xg2J9cBb4EzyMpWZoNCP1BslSAIkOI= github.com/googleinterns/cloud-operations-api-mock v0.0.0-20200709193332-a1e58c29bdd3/go.mod h1:h/KNeRx7oYU4SpA4SoY7W2/NxDKEEVuwA6j9A27L4OI= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -442,7 +460,9 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -455,6 +475,7 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 h1:KUDFlmBg2buRWNzIcwLlKvfcnujcHQRQ1As1LoaCLAM= github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -510,6 +531,7 @@ github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBef 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/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/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= @@ -523,6 +545,7 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= @@ -553,8 +576,11 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/landoop/tableprinter v0.0.0-20200805134727-ea32388e35c1/go.mod h1:f0X1c0za3TbET/rl5ThtCSel0+G3/yZ8iuU9BxnyVK0= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -602,6 +628,7 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 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.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= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -644,6 +671,7 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nicksnyder/go-i18n/v2 v2.1.1 h1:ATCOanRDlrfKVB4WHAdJnLEqZtDmKYsweqsOUYflnBU= github.com/nicksnyder/go-i18n/v2 v2.1.1/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= @@ -653,9 +681,11 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -792,6 +822,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -908,6 +940,7 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1053,6 +1086,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1154,6 +1189,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1231,9 +1267,11 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= @@ -1246,9 +1284,11 @@ gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -1301,6 +1341,7 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kubectl v0.18.3/go.mod h1:k/EpvXBDgEsHBzWr0A44l9+ArvYi3txBBnzXBjQasUQ= k8s.io/metrics v0.18.3/go.mod h1:TkuJE3ezDZ1ym8pYkZoEzJB7HDiFE7qxl+EmExEBoPA= diff --git a/internal/api/authz/context.go b/internal/api/authz/context.go index 62e33421e4..63af939e95 100644 --- a/internal/api/authz/context.go +++ b/internal/api/authz/context.go @@ -37,6 +37,27 @@ type Grant struct { Roles []string } +type Memberships []*Membership + +type Membership struct { + MemberType MemberType + AggregateID string + //ObjectID differs from aggregate id if obejct is sub of an aggregate + ObjectID string + + Roles []string +} + +type MemberType int32 + +const ( + MemberTypeUnspecified MemberType = iota + MemberTypeOrganisation + MemberTypeProject + MemberTypeProjectGrant + MemberTypeIam +) + 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) }() diff --git a/internal/api/authz/permissions.go b/internal/api/authz/permissions.go index 6423f80fd7..c91eeaef55 100644 --- a/internal/api/authz/permissions.go +++ b/internal/api/authz/permissions.go @@ -2,7 +2,6 @@ package authz import ( "context" - "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/telemetry/tracing" ) @@ -16,41 +15,43 @@ func getUserMethodPermissions(ctx context.Context, t *TokenVerifier, requiredPer } ctx = context.WithValue(ctx, dataKey, ctxData) - grant, err := t.ResolveGrant(ctx) + memberships, err := t.SearchMyMemberships(ctx) if err != nil { return nil, nil, err } - if grant == nil { + if len(memberships) == 0 { return requestedPermissions, nil, nil } - requestedPermissions, allPermissions = mapGrantToPermissions(requiredPerm, grant, authConfig) + requestedPermissions, allPermissions = mapMembershipsToPermissions(requiredPerm, memberships, authConfig) return requestedPermissions, allPermissions, nil } -func mapGrantToPermissions(requiredPerm string, grant *Grant, authConfig Config) (requestPermissions, allPermissions []string) { +func mapMembershipsToPermissions(requiredPerm string, memberships []*Membership, 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) + for _, membership := range memberships { + requestPermissions, allPermissions = mapMembershipToPerm(requiredPerm, membership, authConfig, requestPermissions, allPermissions) } return requestPermissions, allPermissions } -func mapRoleToPerm(requiredPerm, actualRole string, authConfig Config, requestPermissions, allPermissions []string) ([]string, []string) { - roleName, roleContextID := SplitPermission(actualRole) - perms := authConfig.getPermissionsFromRole(roleName) +func mapMembershipToPerm(requiredPerm string, membership *Membership, authConfig Config, requestPermissions, allPermissions []string) ([]string, []string) { + roleNames, roleContextID := roleWithContext(membership) + for _, roleName := range roleNames { + perms := authConfig.getPermissionsFromRole(roleName) - for _, p := range perms { - permWithCtx := addRoleContextIDToPerm(p, roleContextID) - if !ExistsPerm(allPermissions, permWithCtx) { - allPermissions = append(allPermissions, permWithCtx) - } + for _, p := range perms { + permWithCtx := addRoleContextIDToPerm(p, roleContextID) + if !ExistsPerm(allPermissions, permWithCtx) { + allPermissions = append(allPermissions, permWithCtx) + } - p, _ = SplitPermission(p) - if p == requiredPerm { - if !ExistsPerm(requestPermissions, permWithCtx) { - requestPermissions = append(requestPermissions, permWithCtx) + p, _ = SplitPermission(p) + if p == requiredPerm { + if !ExistsPerm(requestPermissions, permWithCtx) { + requestPermissions = append(requestPermissions, permWithCtx) + } } } } @@ -72,3 +73,10 @@ func ExistsPerm(existingPermissions []string, perm string) bool { } return false } + +func roleWithContext(membership *Membership) (roles []string, ctxID string) { + if membership.MemberType == MemberTypeProject || membership.MemberType == MemberTypeProjectGrant { + return membership.Roles, membership.ObjectID + } + return membership.Roles, "" +} diff --git a/internal/api/authz/permissions_test.go b/internal/api/authz/permissions_test.go index eed375bdf5..26bdd9805b 100644 --- a/internal/api/authz/permissions_test.go +++ b/internal/api/authz/permissions_test.go @@ -12,15 +12,14 @@ func getTestCtx(userID, orgID string) context.Context { } type testVerifier struct { - grant *Grant + memberships []*Membership } func (v *testVerifier) VerifyAccessToken(ctx context.Context, token, clientID string) (string, string, string, string, error) { return "userID", "agentID", "de", "orgID", nil } - -func (v *testVerifier) ResolveGrants(ctx context.Context) (*Grant, error) { - return v.grant, nil +func (v *testVerifier) SearchMyMemberships(ctx context.Context) ([]*Membership, error) { + return v.memberships, nil } func (v *testVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (string, []string, error) { @@ -65,8 +64,10 @@ func Test_GetUserMethodPermissions(t *testing.T) { name: "Empty Context", args: args{ ctxData: CtxData{}, - verifier: Start(&testVerifier{grant: &Grant{ - Roles: []string{"ORG_OWNER"}, + verifier: Start(&testVerifier{memberships: []*Membership{ + { + Roles: []string{"ORG_OWNER"}, + }, }}), requiredPerm: "project.read", authConfig: Config{ @@ -90,7 +91,7 @@ func Test_GetUserMethodPermissions(t *testing.T) { name: "No Grants", args: args{ ctxData: CtxData{}, - verifier: Start(&testVerifier{grant: &Grant{}}), + verifier: Start(&testVerifier{memberships: []*Membership{}}), requiredPerm: "project.read", authConfig: Config{ RolePermissionMappings: []RoleMapping{ @@ -111,8 +112,13 @@ func Test_GetUserMethodPermissions(t *testing.T) { name: "Get Permissions", args: args{ ctxData: CtxData{UserID: "userID", OrgID: "orgID"}, - verifier: Start(&testVerifier{grant: &Grant{ - Roles: []string{"IAM_OWNER"}, + verifier: Start(&testVerifier{memberships: []*Membership{ + { + AggregateID: "IAM", + ObjectID: "IAM", + MemberType: MemberTypeIam, + Roles: []string{"IAM_OWNER"}, + }, }}), requiredPerm: "project.read", authConfig: Config{ @@ -150,10 +156,10 @@ func Test_GetUserMethodPermissions(t *testing.T) { } } -func Test_MapGrantsToPermissions(t *testing.T) { +func Test_MapMembershipToPermissions(t *testing.T) { type args struct { requiredPerm string - grant *Grant + membership []*Membership authConfig Config } tests := []struct { @@ -166,7 +172,14 @@ func Test_MapGrantsToPermissions(t *testing.T) { name: "One Role existing perm", args: args{ requiredPerm: "project.read", - grant: &Grant{Roles: []string{"ORG_OWNER"}}, + membership: []*Membership{ + { + AggregateID: "1", + ObjectID: "1", + MemberType: MemberTypeOrganisation, + Roles: []string{"ORG_OWNER"}, + }, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -187,7 +200,14 @@ func Test_MapGrantsToPermissions(t *testing.T) { name: "One Role not existing perm", args: args{ requiredPerm: "project.write", - grant: &Grant{Roles: []string{"ORG_OWNER"}}, + membership: []*Membership{ + { + AggregateID: "1", + ObjectID: "1", + MemberType: MemberTypeOrganisation, + Roles: []string{"ORG_OWNER"}, + }, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -208,7 +228,20 @@ func Test_MapGrantsToPermissions(t *testing.T) { name: "Multiple Roles one existing", args: args{ requiredPerm: "project.read", - grant: &Grant{Roles: []string{"ORG_OWNER", "IAM_OWNER"}}, + membership: []*Membership{ + { + AggregateID: "1", + ObjectID: "1", + MemberType: MemberTypeOrganisation, + Roles: []string{"ORG_OWNER"}, + }, + { + AggregateID: "IAM", + ObjectID: "IAM", + MemberType: MemberTypeIam, + Roles: []string{"IAM_OWNER"}, + }, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -229,7 +262,20 @@ func Test_MapGrantsToPermissions(t *testing.T) { name: "Multiple Roles, global and specific", args: args{ requiredPerm: "project.read", - grant: &Grant{Roles: []string{"ORG_OWNER", "PROJECT_OWNER:1"}}, + membership: []*Membership{ + { + AggregateID: "2", + ObjectID: "2", + MemberType: MemberTypeOrganisation, + Roles: []string{"ORG_OWNER"}, + }, + { + AggregateID: "1", + ObjectID: "1", + MemberType: MemberTypeProject, + Roles: []string{"PROJECT_OWNER"}, + }, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -249,7 +295,7 @@ func Test_MapGrantsToPermissions(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - requestPerms, allPerms := mapGrantToPermissions(tt.args.requiredPerm, tt.args.grant, tt.args.authConfig) + requestPerms, allPerms := mapMembershipsToPermissions(tt.args.requiredPerm, tt.args.membership, tt.args.authConfig) if !equalStringArray(requestPerms, tt.requestPerms) { t.Errorf("got wrong requestPerms, expecting: %v, actual: %v ", tt.requestPerms, requestPerms) } @@ -260,10 +306,10 @@ func Test_MapGrantsToPermissions(t *testing.T) { } } -func Test_MapRoleToPerm(t *testing.T) { +func Test_MapMembershipToPerm(t *testing.T) { type args struct { requiredPerm string - actualRole string + membership *Membership authConfig Config requestPerms []string allPerms []string @@ -278,7 +324,12 @@ func Test_MapRoleToPerm(t *testing.T) { name: "first perm without context id", args: args{ requiredPerm: "project.read", - actualRole: "ORG_OWNER", + membership: &Membership{ + AggregateID: "Org", + ObjectID: "Org", + MemberType: MemberTypeOrganisation, + Roles: []string{"ORG_OWNER"}, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -301,7 +352,12 @@ func Test_MapRoleToPerm(t *testing.T) { name: "existing perm without context id", args: args{ requiredPerm: "project.read", - actualRole: "ORG_OWNER", + membership: &Membership{ + AggregateID: "Org", + ObjectID: "Org", + MemberType: MemberTypeOrganisation, + Roles: []string{"ORG_OWNER"}, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -324,7 +380,12 @@ func Test_MapRoleToPerm(t *testing.T) { name: "first perm with context id", args: args{ requiredPerm: "project.read", - actualRole: "PROJECT_OWNER:1", + membership: &Membership{ + AggregateID: "1", + ObjectID: "1", + MemberType: MemberTypeProject, + Roles: []string{"PROJECT_OWNER"}, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -347,7 +408,12 @@ func Test_MapRoleToPerm(t *testing.T) { name: "perm with context id, existing global", args: args{ requiredPerm: "project.read", - actualRole: "PROJECT_OWNER:1", + membership: &Membership{ + AggregateID: "1", + ObjectID: "1", + MemberType: MemberTypeProject, + Roles: []string{"PROJECT_OWNER"}, + }, authConfig: Config{ RolePermissionMappings: []RoleMapping{ { @@ -369,7 +435,7 @@ func Test_MapRoleToPerm(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - requestPerms, allPerms := mapRoleToPerm(tt.args.requiredPerm, tt.args.actualRole, tt.args.authConfig, tt.args.requestPerms, tt.args.allPerms) + requestPerms, allPerms := mapMembershipToPerm(tt.args.requiredPerm, tt.args.membership, tt.args.authConfig, tt.args.requestPerms, tt.args.allPerms) if !equalStringArray(requestPerms, tt.requestPerms) { t.Errorf("got wrong requestPerms, expecting: %v, actual: %v ", tt.requestPerms, requestPerms) } diff --git a/internal/api/authz/token.go b/internal/api/authz/token.go index 352bb44c56..aacba6bba7 100644 --- a/internal/api/authz/token.go +++ b/internal/api/authz/token.go @@ -22,7 +22,7 @@ type TokenVerifier struct { type authZRepo interface { VerifyAccessToken(ctx context.Context, token, clientID string) (userID, agentID, prefLang, resourceOwner string, err error) VerifierClientID(ctx context.Context, name string) (clientID string, err error) - ResolveGrants(ctx context.Context) (grant *Grant, err error) + SearchMyMemberships(ctx context.Context) ([]*Membership, error) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (projectID string, origins []string, err error) ExistsOrg(ctx context.Context, orgID string) error } @@ -86,11 +86,10 @@ func (v *TokenVerifier) clientIDFromMethod(ctx context.Context, method string) ( v.clients.Store(prefix, c) return c.id, nil } - -func (v *TokenVerifier) ResolveGrant(ctx context.Context) (_ *Grant, err error) { +func (v *TokenVerifier) SearchMyMemberships(ctx context.Context) (_ []*Membership, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - return v.authZRepo.ResolveGrants(ctx) + return v.authZRepo.SearchMyMemberships(ctx) } func (v *TokenVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (_ string, _ []string, err error) { diff --git a/internal/api/authz/token_test.go b/internal/api/authz/token_test.go index 7c256086f7..c6a54039a2 100644 --- a/internal/api/authz/token_test.go +++ b/internal/api/authz/token_test.go @@ -43,7 +43,7 @@ func Test_VerifyAccessToken(t *testing.T) { ctx: context.Background(), token: "Bearer AUTH", verifier: &TokenVerifier{ - authZRepo: &testVerifier{grant: &Grant{}}, + authZRepo: &testVerifier{memberships: []*Membership{}}, clients: func() sync.Map { m := sync.Map{} m.Store("service", &client{name: "name"}) diff --git a/internal/api/grpc/auth/user.go b/internal/api/grpc/auth/user.go index f51a06d5af..7aee2371cb 100644 --- a/internal/api/grpc/auth/user.go +++ b/internal/api/grpc/auth/user.go @@ -229,3 +229,13 @@ func (s *Server) GetMyUserChanges(ctx context.Context, request *auth.ChangesRequ } return userChangesToResponse(changes, request.GetSequenceOffset(), request.GetLimit()), nil } + +func (s *Server) SearchMyUserMemberships(ctx context.Context, in *auth.UserMembershipSearchRequest) (*auth.UserMembershipSearchResponse, error) { + request := userMembershipSearchRequestsToModel(in) + request.AppendUserIDQuery(authz.GetCtxData(ctx).UserID) + response, err := s.repo.SearchMyUserMemberships(ctx, request) + if err != nil { + return nil, err + } + return userMembershipSearchResponseFromModel(response), nil +} diff --git a/internal/api/grpc/auth/user_converter.go b/internal/api/grpc/auth/user_converter.go index 07f6302c43..adb1f5358c 100644 --- a/internal/api/grpc/auth/user_converter.go +++ b/internal/api/grpc/auth/user_converter.go @@ -3,7 +3,6 @@ package auth import ( "context" "encoding/json" - "github.com/caos/logging" "github.com/golang/protobuf/ptypes" "golang.org/x/text/language" @@ -462,3 +461,93 @@ func ctxToObjectRoot(ctx context.Context) models.ObjectRoot { ResourceOwner: ctxData.ResourceOwner, } } + +func userMembershipSearchResponseFromModel(response *usr_model.UserMembershipSearchResponse) *auth.UserMembershipSearchResponse { + timestamp, err := ptypes.TimestampProto(response.Timestamp) + logging.Log("GRPC-Hs8jd").OnError(err).Debug("unable to parse timestamp") + return &auth.UserMembershipSearchResponse{ + Offset: response.Offset, + Limit: response.Limit, + TotalResult: response.TotalResult, + Result: userMembershipViewsFromModel(response.Result), + ProcessedSequence: response.Sequence, + ViewTimestamp: timestamp, + } +} + +func userMembershipViewsFromModel(memberships []*usr_model.UserMembershipView) []*auth.UserMembershipView { + converted := make([]*auth.UserMembershipView, len(memberships)) + for i, membership := range memberships { + converted[i] = userMembershipViewFromModel(membership) + } + return converted +} + +func userMembershipViewFromModel(membership *usr_model.UserMembershipView) *auth.UserMembershipView { + creationDate, err := ptypes.TimestampProto(membership.CreationDate) + logging.Log("GRPC-Msnu8").OnError(err).Debug("unable to parse timestamp") + + changeDate, err := ptypes.TimestampProto(membership.ChangeDate) + logging.Log("GRPC-Slco9").OnError(err).Debug("unable to parse timestamp") + + return &auth.UserMembershipView{ + UserId: membership.UserID, + AggregateId: membership.AggregateID, + ObjectId: membership.ObjectID, + MemberType: memberTypeFromModel(membership.MemberType), + DisplayName: membership.DisplayName, + Roles: membership.Roles, + CreationDate: creationDate, + ChangeDate: changeDate, + Sequence: membership.Sequence, + ResourceOwner: membership.ResourceOwner, + } +} + +func userMembershipSearchRequestsToModel(request *auth.UserMembershipSearchRequest) *usr_model.UserMembershipSearchRequest { + return &usr_model.UserMembershipSearchRequest{ + Offset: request.Offset, + Limit: request.Limit, + Queries: userMembershipSearchQueriesToModel(request.Queries), + } +} + +func userMembershipSearchQueriesToModel(queries []*auth.UserMembershipSearchQuery) []*usr_model.UserMembershipSearchQuery { + converted := make([]*usr_model.UserMembershipSearchQuery, len(queries)) + for i, q := range queries { + converted[i] = userMembershipSearchQueryToModel(q) + } + return converted +} + +func userMembershipSearchQueryToModel(query *auth.UserMembershipSearchQuery) *usr_model.UserMembershipSearchQuery { + return &usr_model.UserMembershipSearchQuery{ + Key: userMembershipSearchKeyToModel(query.Key), + Method: searchMethodToModel(query.Method), + Value: query.Value, + } +} + +func userMembershipSearchKeyToModel(key auth.UserMembershipSearchKey) usr_model.UserMembershipSearchKey { + switch key { + case auth.UserMembershipSearchKey_USERMEMBERSHIPSEARCHKEY_TYPE: + return usr_model.UserMembershipSearchKeyMemberType + case auth.UserMembershipSearchKey_USERMEMBERSHIPSEARCHKEY_OBJECT_ID: + return usr_model.UserMembershipSearchKeyObjectID + default: + return usr_model.UserMembershipSearchKeyUnspecified + } +} + +func memberTypeFromModel(memberType usr_model.MemberType) auth.MemberType { + switch memberType { + case usr_model.MemberTypeOrganisation: + return auth.MemberType_MEMBERTYPE_ORGANISATION + case usr_model.MemberTypeProject: + return auth.MemberType_MEMBERTYPE_PROJECT + case usr_model.MemberTypeProjectGrant: + return auth.MemberType_MEMBERTYPE_PROJECT_GRANT + default: + return auth.MemberType_MEMBERTYPE_UNSPECIFIED + } +} diff --git a/internal/api/grpc/auth/user_human_converter.go b/internal/api/grpc/auth/user_human_converter.go index e3e077001d..7d292a9454 100644 --- a/internal/api/grpc/auth/user_human_converter.go +++ b/internal/api/grpc/auth/user_human_converter.go @@ -3,7 +3,7 @@ package auth import ( "github.com/caos/logging" usr_model "github.com/caos/zitadel/internal/user/model" - auth "github.com/caos/zitadel/pkg/grpc/auth" + "github.com/caos/zitadel/pkg/grpc/auth" "github.com/golang/protobuf/ptypes" ) diff --git a/internal/api/grpc/auth/user_machine_converter.go b/internal/api/grpc/auth/user_machine_converter.go index b868354d35..003c0eee11 100644 --- a/internal/api/grpc/auth/user_machine_converter.go +++ b/internal/api/grpc/auth/user_machine_converter.go @@ -2,9 +2,10 @@ package auth import ( "github.com/caos/logging" + "github.com/golang/protobuf/ptypes" + usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/pkg/grpc/auth" - "github.com/golang/protobuf/ptypes" ) func machineViewFromModel(machine *usr_model.MachineView) *auth.MachineView { @@ -16,36 +17,3 @@ func machineViewFromModel(machine *usr_model.MachineView) *auth.MachineView { LastKeyAdded: lastKeyAdded, } } - -func machineKeyViewsFromModel(keys ...*usr_model.MachineKeyView) []*auth.MachineKeyView { - keyViews := make([]*auth.MachineKeyView, len(keys)) - for i, key := range keys { - keyViews[i] = machineKeyViewFromModel(key) - } - return keyViews -} - -func machineKeyViewFromModel(key *usr_model.MachineKeyView) *auth.MachineKeyView { - creationDate, err := ptypes.TimestampProto(key.CreationDate) - logging.Log("MANAG-gluk7").OnError(err).Debug("unable to parse timestamp") - - expirationDate, err := ptypes.TimestampProto(key.CreationDate) - logging.Log("MANAG-gluk7").OnError(err).Debug("unable to parse timestamp") - - return &auth.MachineKeyView{ - Id: key.ID, - CreationDate: creationDate, - ExpirationDate: expirationDate, - Sequence: key.Sequence, - Type: machineKeyTypeFromModel(key.Type), - } -} - -func machineKeyTypeFromModel(typ usr_model.MachineKeyType) auth.MachineKeyType { - switch typ { - case usr_model.MachineKeyTypeJSON: - return auth.MachineKeyType_MACHINEKEY_JSON - default: - return auth.MachineKeyType_MACHINEKEY_UNSPECIFIED - } -} diff --git a/internal/api/grpc/management/application.go b/internal/api/grpc/management/application.go index f7a6d35758..6e0bc7293a 100644 --- a/internal/api/grpc/management/application.go +++ b/internal/api/grpc/management/application.go @@ -32,6 +32,13 @@ func (s *Server) CreateOIDCApplication(ctx context.Context, in *management.OIDCA } return oidcAppFromDomain(app), nil } +func (s *Server) CreateAPIApplication(ctx context.Context, in *management.APIApplicationCreate) (*management.Application, error) { + app, err := s.project.AddApplication(ctx, apiAppCreateToModel(in)) + if err != nil { + return nil, err + } + return appFromModel(app), nil +} func (s *Server) UpdateApplication(ctx context.Context, in *management.ApplicationUpdate) (*management.Application, error) { app, err := s.command.ChangeApplication(ctx, in.ProjectId, appUpdateToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { @@ -61,6 +68,14 @@ func (s *Server) UpdateApplicationOIDCConfig(ctx context.Context, in *management return oidcConfigFromDomain(config), nil } +func (s *Server) UpdateApplicationAPIConfig(ctx context.Context, in *management.APIConfigUpdate) (*management.APIConfig, error) { + config, err := s.project.ChangeAPIConfig(ctx, apiConfigUpdateToModel(in)) + if err != nil { + return nil, err + } + return apiConfigFromModel(config), nil +} + func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, in *management.ApplicationID) (*management.ClientSecret, error) { config, err := s.command.ChangeOIDCApplicationSecret(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).ResourceOwner) if err != nil { @@ -69,6 +84,14 @@ func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, in *management. return &management.ClientSecret{ClientSecret: config.ClientSecretString}, nil } +func (s *Server) RegenerateAPIClientSecret(ctx context.Context, in *management.ApplicationID) (*management.ClientSecret, error) { + config, err := s.project.ChangeAPIConfigSecret(ctx, in.ProjectId, in.Id) + if err != nil { + return nil, err + } + return &management.ClientSecret{ClientSecret: config.ClientSecretString}, nil +} + func (s *Server) ApplicationChanges(ctx context.Context, changesRequest *management.ChangeRequest) (*management.Changes, error) { response, err := s.project.ApplicationChanges(ctx, changesRequest.Id, changesRequest.SecId, changesRequest.SequenceOffset, changesRequest.Limit, changesRequest.Asc) if err != nil { @@ -76,3 +99,32 @@ func (s *Server) ApplicationChanges(ctx context.Context, changesRequest *managem } return appChangesToResponse(response, changesRequest.GetSequenceOffset(), changesRequest.GetLimit()), nil } + +func (s *Server) SearchClientKeys(ctx context.Context, req *management.ClientKeySearchRequest) (*management.ClientKeySearchResponse, error) { + result, err := s.project.SearchClientKeys(ctx, clientKeySearchRequestToModel(req)) + if err != nil { + return nil, err + } + return clientKeySearchResponseFromModel(result), nil +} + +func (s *Server) GetClientKey(ctx context.Context, req *management.ClientKeyIDRequest) (*management.ClientKeyView, error) { + key, err := s.project.GetClientKey(ctx, req.ProjectId, req.ApplicationId, req.KeyId) + if err != nil { + return nil, err + } + return clientKeyViewFromModel(key), nil +} + +func (s *Server) AddClientKey(ctx context.Context, req *management.AddClientKeyRequest) (*management.AddClientKeyResponse, error) { + key, err := s.project.AddClientKey(ctx, addClientKeyToModel(req)) + if err != nil { + return nil, err + } + return addClientKeyFromModel(key), nil +} + +func (s *Server) DeleteClientKey(ctx context.Context, req *management.ClientKeyIDRequest) (*empty.Empty, error) { + err := s.project.RemoveClientKey(ctx, req.ProjectId, req.ApplicationId, req.KeyId) + return &empty.Empty{}, err +} diff --git a/internal/api/grpc/management/application_converter.go b/internal/api/grpc/management/application_converter.go index 639c270144..2c0cc243ac 100644 --- a/internal/api/grpc/management/application_converter.go +++ b/internal/api/grpc/management/application_converter.go @@ -2,18 +2,20 @@ package management import ( "encoding/json" - "github.com/caos/zitadel/internal/v2/domain" - "google.golang.org/protobuf/types/known/timestamppb" + "time" "github.com/caos/logging" "github.com/golang/protobuf/ptypes" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/caos/zitadel/internal/eventstore/models" + key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/model" proj_model "github.com/caos/zitadel/internal/project/model" + "github.com/caos/zitadel/internal/v2/domain" "github.com/caos/zitadel/pkg/grpc/management" "github.com/caos/zitadel/pkg/grpc/message" ) @@ -25,6 +27,28 @@ func appFromDomain(app domain.Application) *management.Application { Name: app.GetApplicationName(), } } +func appFromModel(app *proj_model.Application) *management.Application { + changeDate, err := ptypes.TimestampProto(app.ChangeDate) + logging.Log("GRPC-di7rw").OnError(err).Debug("unable to parse timestamp") + + return &management.Application{ + Id: app.AppID, + State: appStateFromModel(app.State), + ChangeDate: changeDate, + Name: app.Name, + Sequence: app.Sequence, + AppConfig: appConfigFromModel(app), + } +} + +func appConfigFromModel(app *proj_model.Application) management.AppConfig { + if app.Type == proj_model.AppTypeAPI { + return &management.Application_ApiConfig{ + ApiConfig: apiConfigFromModel(app.APIConfig), + } + } + return nil +} func oidcAppFromDomain(app *domain.OIDCApp) *management.Application { return &management.Application{ @@ -41,6 +65,7 @@ func oidcAppConfigFromDomain(app *domain.OIDCApp) management.AppConfig { return &management.Application_OidcConfig{ OidcConfig: oidcConfigFromDomain(app), } + return nil } func oidcConfigFromDomain(config *domain.OIDCApp) *management.OIDCConfig { @@ -65,6 +90,14 @@ func oidcConfigFromDomain(config *domain.OIDCApp) *management.OIDCConfig { } } +func apiConfigFromModel(config *proj_model.APIConfig) *management.APIConfig { + return &management.APIConfig{ + ClientId: config.ClientID, + ClientSecret: config.ClientSecretString, + AuthMethodType: apiAuthMethodTypeFromModel(config.AuthMethodType), + } +} + func oidcConfigFromApplicationViewModel(app *proj_model.ApplicationView) *management.OIDCConfig { return &management.OIDCConfig{ RedirectUris: app.OIDCRedirectUris, @@ -74,11 +107,11 @@ func oidcConfigFromApplicationViewModel(app *proj_model.ApplicationView) *manage ClientId: app.OIDCClientID, AuthMethodType: oidcAuthMethodTypeFromModel(app.OIDCAuthMethodType), PostLogoutRedirectUris: app.OIDCPostLogoutRedirectUris, - Version: oidcVersionFromModel(app.OIDCVersion), + Version: oidcVersionFromDomain(domain.OIDCVersion(app.OIDCVersion)), NoneCompliant: app.NoneCompliant, ComplianceProblems: complianceProblemsToLocalizedMessages(app.ComplianceProblems), DevMode: app.DevMode, - AccessTokenType: oidcTokenTypeFromModel(app.AccessTokenType), + AccessTokenType: oidcTokenTypeFromDomain(domain.OIDCTokenType(app.AccessTokenType)), AccessTokenRoleAssertion: app.AccessTokenRoleAssertion, IdTokenRoleAssertion: app.IDTokenRoleAssertion, IdTokenUserinfoAssertion: app.IDTokenUserinfoAssertion, @@ -124,6 +157,29 @@ func appUpdateToDomain(app *management.ApplicationUpdate) domain.Application { } } +func apiAppCreateToModel(app *management.APIApplicationCreate) *proj_model.Application { + return &proj_model.Application{ + ObjectRoot: models.ObjectRoot{ + AggregateID: app.ProjectId, + }, + Name: app.Name, + Type: proj_model.AppTypeAPI, + APIConfig: &proj_model.APIConfig{ + AuthMethodType: apiAuthMethodTypeToModel(app.AuthMethodType), + }, + } +} + +func appUpdateToModel(app *management.ApplicationUpdate) *proj_model.Application { + return &proj_model.Application{ + ObjectRoot: models.ObjectRoot{ + AggregateID: app.ProjectId, + }, + AppID: app.Id, + Name: app.Name, + } +} + func oidcConfigUpdateToDomain(app *management.OIDCConfigUpdate) *domain.OIDCApp { return &domain.OIDCApp{ ObjectRoot: models.ObjectRoot{ @@ -145,6 +201,16 @@ func oidcConfigUpdateToDomain(app *management.OIDCConfigUpdate) *domain.OIDCApp } } +func apiConfigUpdateToModel(app *management.APIConfigUpdate) *proj_model.APIConfig { + return &proj_model.APIConfig{ + ObjectRoot: models.ObjectRoot{ + AggregateID: app.ProjectId, + }, + AppID: app.ApplicationId, + AuthMethodType: apiAuthMethodTypeToModel(app.AuthMethodType), + } +} + func applicationSearchRequestsToModel(request *management.ApplicationSearchRequest) *proj_model.ApplicationSearchRequest { return &proj_model.ApplicationSearchRequest{ Offset: request.Offset, @@ -404,6 +470,8 @@ func oidcAuthMethodTypeToDomain(authType management.OIDCAuthMethodType) domain.O return domain.OIDCAuthMethodTypePost case management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_NONE: return domain.OIDCAuthMethodTypeNone + case management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT: + return domain.OIDCAuthMethodTypePrivateKeyJWT default: return domain.OIDCAuthMethodTypeBasic } @@ -422,6 +490,17 @@ func oidcAuthMethodTypeFromDomain(authType domain.OIDCAuthMethodType) management } } +func apiAuthMethodTypeToModel(authType management.APIAuthMethodType) proj_model.APIAuthMethodType { + switch authType { + case management.APIAuthMethodType_APIAUTHMETHODTYPE_BASIC: + return proj_model.APIAuthMethodTypeBasic + case management.APIAuthMethodType_APIAUTHMETHODTYPE_PRIVATE_KEY_JWT: + return proj_model.APIAuthMethodTypePrivateKeyJWT + default: + return proj_model.APIAuthMethodTypeBasic + } +} + func oidcAuthMethodTypeFromModel(authType proj_model.OIDCAuthMethodType) management.OIDCAuthMethodType { switch authType { case proj_model.OIDCAuthMethodTypeBasic: @@ -430,6 +509,8 @@ func oidcAuthMethodTypeFromModel(authType proj_model.OIDCAuthMethodType) managem return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_POST case proj_model.OIDCAuthMethodTypeNone: return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_NONE + case proj_model.OIDCAuthMethodTypePrivateKeyJWT: + return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT default: return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_BASIC } @@ -457,14 +538,14 @@ func oidcTokenTypeFromDomain(tokenType domain.OIDCTokenType) management.OIDCToke } } -func oidcTokenTypeFromModel(tokenType proj_model.OIDCTokenType) management.OIDCTokenType { - switch tokenType { - case proj_model.OIDCTokenTypeBearer: - return management.OIDCTokenType_OIDCTokenType_Bearer - case proj_model.OIDCTokenTypeJWT: - return management.OIDCTokenType_OIDCTokenType_JWT +func apiAuthMethodTypeFromModel(authType proj_model.APIAuthMethodType) management.APIAuthMethodType { + switch authType { + case proj_model.APIAuthMethodTypeBasic: + return management.APIAuthMethodType_APIAUTHMETHODTYPE_BASIC + case proj_model.APIAuthMethodTypePrivateKeyJWT: + return management.APIAuthMethodType_APIAUTHMETHODTYPE_PRIVATE_KEY_JWT default: - return management.OIDCTokenType_OIDCTokenType_Bearer + return management.APIAuthMethodType_APIAUTHMETHODTYPE_BASIC } } @@ -477,15 +558,6 @@ func oidcVersionFromDomain(version domain.OIDCVersion) management.OIDCVersion { } } -func oidcVersionFromModel(version proj_model.OIDCVersion) management.OIDCVersion { - switch version { - case proj_model.OIDCVersionV1: - return management.OIDCVersion_OIDCV1_0 - default: - return management.OIDCVersion_OIDCV1_0 - } -} - func appChangesToResponse(response *proj_model.ApplicationChanges, offset uint64, limit uint64) (_ *management.Changes) { return &management.Changes{ Limit: limit, @@ -515,3 +587,126 @@ func appChangesToMgtAPI(changes *proj_model.ApplicationChanges) (_ []*management return result } + +func clientKeyViewsFromModel(keys ...*key_model.AuthNKeyView) []*management.ClientKeyView { + keyViews := make([]*management.ClientKeyView, len(keys)) + for i, key := range keys { + keyViews[i] = clientKeyViewFromModel(key) + } + return keyViews +} + +func clientKeyViewFromModel(key *key_model.AuthNKeyView) *management.ClientKeyView { + creationDate, err := ptypes.TimestampProto(key.CreationDate) + logging.Log("MANAG-DAs2t").OnError(err).Debug("unable to parse timestamp") + + expirationDate, err := ptypes.TimestampProto(key.ExpirationDate) + logging.Log("MANAG-BDgh4").OnError(err).Debug("unable to parse timestamp") + + return &management.ClientKeyView{ + Id: key.ID, + CreationDate: creationDate, + ExpirationDate: expirationDate, + Sequence: key.Sequence, + Type: authNKeyTypeFromModel(key.Type), + } +} + +func addClientKeyToModel(key *management.AddClientKeyRequest) *proj_model.ClientKey { + expirationDate := time.Time{} + if key.ExpirationDate != nil { + var err error + expirationDate, err = ptypes.Timestamp(key.ExpirationDate) + logging.Log("MANAG-Dgt42").OnError(err).Debug("unable to parse expiration date") + } + + return &proj_model.ClientKey{ + ExpirationDate: expirationDate, + Type: authNKeyTypeToModel(key.Type), + ApplicationID: key.ApplicationId, + ObjectRoot: models.ObjectRoot{AggregateID: key.ProjectId}, + } +} + +func addClientKeyFromModel(key *proj_model.ClientKey) *management.AddClientKeyResponse { + creationDate, err := ptypes.TimestampProto(key.CreationDate) + logging.Log("MANAG-FBzz4").OnError(err).Debug("unable to parse cretaion date") + + expirationDate, err := ptypes.TimestampProto(key.ExpirationDate) + logging.Log("MANAG-sag21").OnError(err).Debug("unable to parse cretaion date") + + detail, err := json.Marshal(struct { + Type string `json:"type"` + KeyID string `json:"keyId"` + Key string `json:"key"` + AppID string `json:"appId"` + ClientID string `json:"clientID"` + }{ + Type: "application", + KeyID: key.KeyID, + Key: string(key.PrivateKey), + AppID: key.ApplicationID, + ClientID: key.ClientID, + }) + logging.Log("MANAG-adt42").OnError(err).Warn("unable to marshall key") + + return &management.AddClientKeyResponse{ + Id: key.KeyID, + CreationDate: creationDate, + ExpirationDate: expirationDate, + Sequence: key.Sequence, + KeyDetails: detail, + Type: authNKeyTypeFromModel(key.Type), + } +} + +func authNKeyTypeToModel(typ management.AuthNKeyType) key_model.AuthNKeyType { + switch typ { + case management.AuthNKeyType_AUTHNKEY_JSON: + return key_model.AuthNKeyTypeJSON + default: + return key_model.AuthNKeyTypeNONE + } +} + +func authNKeyTypeFromModel(typ key_model.AuthNKeyType) management.AuthNKeyType { + switch typ { + case key_model.AuthNKeyTypeJSON: + return management.AuthNKeyType_AUTHNKEY_JSON + default: + return management.AuthNKeyType_AUTHNKEY_UNSPECIFIED + } +} + +func clientKeySearchRequestToModel(req *management.ClientKeySearchRequest) *key_model.AuthNKeySearchRequest { + return &key_model.AuthNKeySearchRequest{ + Offset: req.Offset, + Limit: req.Limit, + Asc: req.Asc, + Queries: []*key_model.AuthNKeySearchQuery{ + { + Key: key_model.AuthNKeyObjectType, + Method: model.SearchMethodEquals, + Value: key_model.AuthNKeyObjectTypeApplication, + }, { + Key: key_model.AuthNKeyObjectID, + Method: model.SearchMethodEquals, + Value: req.ApplicationId, + }, + }, + } +} + +func clientKeySearchResponseFromModel(req *key_model.AuthNKeySearchResponse) *management.ClientKeySearchResponse { + viewTimestamp, err := ptypes.TimestampProto(req.Timestamp) + logging.Log("MANAG-Sk9ds").OnError(err).Debug("unable to parse cretaion date") + + return &management.ClientKeySearchResponse{ + Offset: req.Offset, + Limit: req.Limit, + TotalResult: req.TotalResult, + ProcessedSequence: req.Sequence, + ViewTimestamp: viewTimestamp, + Result: clientKeyViewsFromModel(req.Result...), + } +} diff --git a/internal/api/grpc/management/user_machine_converter.go b/internal/api/grpc/management/user_machine_converter.go index fd1d651d68..cfb19844cd 100644 --- a/internal/api/grpc/management/user_machine_converter.go +++ b/internal/api/grpc/management/user_machine_converter.go @@ -12,6 +12,7 @@ import ( "github.com/golang/protobuf/ptypes" "github.com/caos/zitadel/internal/eventstore/models" + key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/model" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/pkg/grpc/management" @@ -52,7 +53,7 @@ func machineViewFromModel(machine *usr_model.MachineView) *management.MachineVie } } -func machineKeyViewsFromModel(keys ...*usr_model.MachineKeyView) []*management.MachineKeyView { +func authnKeyViewsFromModel(keys ...*key_model.AuthNKeyView) []*management.MachineKeyView { keyViews := make([]*management.MachineKeyView, len(keys)) for i, key := range keys { keyViews[i] = machineKeyViewFromModel(key) @@ -60,7 +61,7 @@ func machineKeyViewsFromModel(keys ...*usr_model.MachineKeyView) []*management.M return keyViews } -func machineKeyViewFromModel(key *usr_model.MachineKeyView) *management.MachineKeyView { +func machineKeyViewFromModel(key *key_model.AuthNKeyView) *management.MachineKeyView { creationDate, err := ptypes.TimestampProto(key.CreationDate) logging.Log("MANAG-gluk7").OnError(err).Debug("unable to parse timestamp") @@ -133,23 +134,27 @@ func machineKeyTypeFromDomain(typ domain.MachineKeyType) management.MachineKeyTy } } -func machineKeyTypeFromModel(typ usr_model.MachineKeyType) management.MachineKeyType { +func machineKeyTypeFromModel(typ key_model.AuthNKeyType) management.MachineKeyType { switch typ { - case usr_model.MachineKeyTypeJSON: + case key_model.AuthNKeyTypeJSON: return management.MachineKeyType_MACHINEKEY_JSON default: return management.MachineKeyType_MACHINEKEY_UNSPECIFIED } } -func machineKeySearchRequestToModel(req *management.MachineKeySearchRequest) *usr_model.MachineKeySearchRequest { - return &usr_model.MachineKeySearchRequest{ +func machineKeySearchRequestToModel(req *management.MachineKeySearchRequest) *key_model.AuthNKeySearchRequest { + return &key_model.AuthNKeySearchRequest{ Offset: req.Offset, Limit: req.Limit, Asc: req.Asc, - Queries: []*usr_model.MachineKeySearchQuery{ + Queries: []*key_model.AuthNKeySearchQuery{ { - Key: usr_model.MachineKeyKeyUserID, + Key: key_model.AuthNKeyObjectType, + Method: model.SearchMethodEquals, + Value: key_model.AuthNKeyObjectTypeUser, + }, { + Key: key_model.AuthNKeyObjectID, Method: model.SearchMethodEquals, Value: req.UserId, }, @@ -157,7 +162,7 @@ func machineKeySearchRequestToModel(req *management.MachineKeySearchRequest) *us } } -func machineKeySearchResponseFromModel(req *usr_model.MachineKeySearchResponse) *management.MachineKeySearchResponse { +func machineKeySearchResponseFromModel(req *key_model.AuthNKeySearchResponse) *management.MachineKeySearchResponse { viewTimestamp, err := ptypes.TimestampProto(req.Timestamp) logging.Log("MANAG-Sk9ds").OnError(err).Debug("unable to parse cretaion date") @@ -167,6 +172,6 @@ func machineKeySearchResponseFromModel(req *usr_model.MachineKeySearchResponse) TotalResult: req.TotalResult, ProcessedSequence: req.Sequence, ViewTimestamp: viewTimestamp, - Result: machineKeyViewsFromModel(req.Result...), + Result: authnKeyViewsFromModel(req.Result...), } } diff --git a/internal/api/grpc/server/middleware/auth_interceptor_test.go b/internal/api/grpc/server/middleware/auth_interceptor_test.go index 671e64f3a8..5f02048f8c 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor_test.go +++ b/internal/api/grpc/server/middleware/auth_interceptor_test.go @@ -24,9 +24,10 @@ type verifierMock struct{} func (v *verifierMock) VerifyAccessToken(ctx context.Context, token, clientID string) (string, string, string, string, error) { return "", "", "", "", nil } -func (v *verifierMock) ResolveGrants(ctx context.Context) (*authz.Grant, error) { +func (v *verifierMock) SearchMyMemberships(ctx context.Context) ([]*authz.Membership, error) { return nil, nil } + func (v *verifierMock) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (string, []string, error) { return "", nil, nil } diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 39fffdba0d..8d7a653a90 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -12,6 +12,7 @@ import ( "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/http" "github.com/caos/zitadel/internal/auth_request/model" + authreq_model "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" @@ -55,13 +56,17 @@ func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (_ op.Cl } func (o *OPStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (_ *jose.JSONWebKey, err error) { + return o.GetKeyByIDAndIssuer(ctx, keyID, userID) +} + +func (o *OPStorage) GetKeyByIDAndIssuer(ctx context.Context, keyID, issuer string) (_ *jose.JSONWebKey, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() key, err := o.repo.MachineKeyByID(ctx, keyID) if err != nil { return nil, err } - if key.UserID != userID { + if key.AuthIdentifier != issuer { return nil, errors.ThrowPermissionDenied(nil, "OIDC-24jm3", "key from different user") } publicKey, err := crypto.BytesToPublicKey(key.PublicKey) @@ -75,6 +80,29 @@ func (o *OPStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID strin }, nil } +func (o *OPStorage) ValidateJWTProfileScopes(ctx context.Context, subject string, scopes oidc.Scopes) (oidc.Scopes, error) { + user, err := o.repo.UserByID(ctx, subject) + if err != nil { + return nil, err + } + for i := len(scopes) - 1; i >= 0; i-- { + scope := scopes[i] + if strings.HasPrefix(scope, authreq_model.OrgDomainPrimaryScope) { + var orgID string + org, err := o.repo.OrgByPrimaryDomain(strings.TrimPrefix(scope, authreq_model.OrgDomainPrimaryScope)) + if err == nil { + orgID = org.ID + } + if orgID != user.ResourceOwner { + scopes[i] = scopes[len(scopes)-1] + scopes[len(scopes)-1] = "" + scopes = scopes[:len(scopes)-1] + } + } + } + return scopes, nil +} + func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secret string) (err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -85,33 +113,32 @@ func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secr return o.repo.AuthorizeOIDCApplication(ctx, id, secret) } -func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID, subject, origin string) (_ oidc.UserInfo, err error) { +func (o *OPStorage) SetUserinfoFromToken(ctx context.Context, userInfo oidc.UserInfoSetter, tokenID, subject, origin string) (err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() token, err := o.repo.TokenByID(ctx, subject, tokenID) if err != nil { - return nil, errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired") + return errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired") } if token.ApplicationID != "" { app, err := o.repo.ApplicationByClientID(ctx, token.ApplicationID) if err != nil { - return nil, err + return err } if origin != "" && !http.IsOriginAllowed(app.OriginAllowList, origin) { - return nil, errors.ThrowPermissionDenied(nil, "OIDC-da1f3", "origin is not allowed") + return errors.ThrowPermissionDenied(nil, "OIDC-da1f3", "origin is not allowed") } } - return o.GetUserinfoFromScopes(ctx, token.UserID, token.ApplicationID, token.Scopes) + return o.SetUserinfoFromScopes(ctx, userInfo, token.UserID, token.ApplicationID, token.Scopes) } -func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID, applicationID string, scopes []string) (_ oidc.UserInfo, err error) { +func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo oidc.UserInfoSetter, userID, applicationID string, scopes []string) (err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() user, err := o.repo.UserByID(ctx, userID) if err != nil { - return nil, err + return err } - userInfo := oidc.NewUserInfo() roles := make([]string, 0) for _, scope := range scopes { switch scope { @@ -160,17 +187,40 @@ func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID, applicati } if len(roles) == 0 || applicationID == "" { - return userInfo, nil + return nil } projectRoles, err := o.assertRoles(ctx, userID, applicationID, roles) if err != nil { - return nil, err + return err } if len(projectRoles) > 0 { userInfo.AppendClaims(ClaimProjectRoles, projectRoles) } - return userInfo, nil + return nil +} + +func (o *OPStorage) SetIntrospectionFromToken(ctx context.Context, introspection oidc.IntrospectionResponse, tokenID, subject, clientID string) error { + token, err := o.repo.TokenByID(ctx, subject, tokenID) + if err != nil { + return errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired") + } + app, err := o.repo.ApplicationByClientID(ctx, clientID) + if err != nil { + return errors.ThrowPermissionDenied(nil, "OIDC-Adfg5", "client not found") + } + for _, aud := range token.Audience { + if aud == clientID || aud == app.ProjectID { + err := o.SetUserinfoFromScopes(ctx, introspection, token.UserID, clientID, token.Scopes) + if err != nil { + return err + } + introspection.SetScopes(token.Scopes) + introspection.SetClientID(token.ApplicationID) + return nil + } + } + return errors.ThrowPermissionDenied(nil, "OIDC-sdg3G", "token is not valid for this client") } func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (claims map[string]interface{}, err error) { diff --git a/internal/api/oidc/client_converter.go b/internal/api/oidc/client_converter.go index a1d9153826..6098dd43c7 100644 --- a/internal/api/oidc/client_converter.go +++ b/internal/api/oidc/client_converter.go @@ -1,13 +1,13 @@ package oidc import ( - authreq_model "github.com/caos/zitadel/internal/auth_request/model" "strings" "time" "github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/op" + authreq_model "github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/project/model" ) @@ -37,7 +37,7 @@ func (c *Client) ApplicationType() op.ApplicationType { return op.ApplicationType(c.OIDCApplicationType) } -func (c *Client) AuthMethod() op.AuthMethod { +func (c *Client) AuthMethod() oidc.AuthMethod { return authMethodToOIDC(c.OIDCAuthMethodType) } @@ -129,16 +129,18 @@ func accessTokenTypeToOIDC(tokenType model.OIDCTokenType) op.AccessTokenType { } } -func authMethodToOIDC(authType model.OIDCAuthMethodType) op.AuthMethod { +func authMethodToOIDC(authType model.OIDCAuthMethodType) oidc.AuthMethod { switch authType { case model.OIDCAuthMethodTypeBasic: - return op.AuthMethodBasic + return oidc.AuthMethodBasic case model.OIDCAuthMethodTypePost: - return op.AuthMethodPost + return oidc.AuthMethodPost case model.OIDCAuthMethodTypeNone: - return op.AuthMethodNone + return oidc.AuthMethodNone + case model.OIDCAuthMethodTypePrivateKeyJWT: + return oidc.AuthMethodPrivateKeyJWT default: - return op.AuthMethodBasic + return oidc.AuthMethodBasic } } diff --git a/internal/api/oidc/op.go b/internal/api/oidc/op.go index de1ffa4f04..1de8f8a206 100644 --- a/internal/api/oidc/op.go +++ b/internal/api/oidc/op.go @@ -2,9 +2,6 @@ package oidc import ( "context" - "github.com/caos/zitadel/internal/telemetry/metrics" - "github.com/caos/zitadel/internal/v2/command" - "github.com/caos/zitadel/internal/v2/query" "time" "github.com/caos/logging" @@ -15,7 +12,10 @@ import ( "github.com/caos/zitadel/internal/auth/repository" "github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/id" + "github.com/caos/zitadel/internal/telemetry/metrics" "github.com/caos/zitadel/internal/telemetry/tracing" + "github.com/caos/zitadel/internal/v2/command" + "github.com/caos/zitadel/internal/v2/query" ) type OPHandlerConfig struct { @@ -34,11 +34,12 @@ type StorageConfig struct { } type EndpointConfig struct { - Auth *Endpoint - Token *Endpoint - Userinfo *Endpoint - EndSession *Endpoint - Keys *Endpoint + Auth *Endpoint + Token *Endpoint + Introspection *Endpoint + Userinfo *Endpoint + EndSession *Endpoint + Keys *Endpoint } type Endpoint struct { @@ -74,6 +75,7 @@ func NewProvider(ctx context.Context, config OPHandlerConfig, command *command.C ), op.WithCustomAuthEndpoint(op.NewEndpointWithURL(config.Endpoints.Auth.Path, config.Endpoints.Auth.URL)), op.WithCustomTokenEndpoint(op.NewEndpointWithURL(config.Endpoints.Token.Path, config.Endpoints.Token.URL)), + op.WithCustomIntrospectionEndpoint(op.NewEndpointWithURL(config.Endpoints.Introspection.Path, config.Endpoints.Introspection.URL)), op.WithCustomUserinfoEndpoint(op.NewEndpointWithURL(config.Endpoints.Userinfo.Path, config.Endpoints.Userinfo.URL)), op.WithCustomEndSessionEndpoint(op.NewEndpointWithURL(config.Endpoints.EndSession.Path, config.Endpoints.EndSession.URL)), op.WithCustomKeysEndpoint(op.NewEndpointWithURL(config.Endpoints.Keys.Path, config.Endpoints.Keys.URL)), diff --git a/internal/auth/repository/eventsourcing/eventstore/org.go b/internal/auth/repository/eventsourcing/eventstore/org.go index 062410e585..83313647b1 100644 --- a/internal/auth/repository/eventsourcing/eventstore/org.go +++ b/internal/auth/repository/eventsourcing/eventstore/org.go @@ -4,6 +4,7 @@ import ( "context" "github.com/caos/logging" + "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/errors" @@ -52,6 +53,14 @@ func (repo *OrgRepository) SearchOrgs(ctx context.Context, request *org_model.Or return result, nil } +func (repo *OrgRepository) OrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) { + org, err := repo.View.OrgByPrimaryDomain(primaryDomain) + if err != nil { + return nil, err + } + return model.OrgToModel(org), nil +} + func (repo *OrgRepository) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) { orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID) if err != nil { diff --git a/internal/auth/repository/eventsourcing/eventstore/token.go b/internal/auth/repository/eventsourcing/eventstore/token.go index d71523c98a..47b040b3ed 100644 --- a/internal/auth/repository/eventsourcing/eventstore/token.go +++ b/internal/auth/repository/eventsourcing/eventstore/token.go @@ -2,20 +2,23 @@ package eventstore import ( "context" + "time" + "github.com/caos/logging" + + "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/errors" + proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" "github.com/caos/zitadel/internal/telemetry/tracing" usr_model "github.com/caos/zitadel/internal/user/model" user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" "github.com/caos/zitadel/internal/user/repository/view/model" - "time" - - "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" ) type TokenRepo struct { - UserEvents *user_event.UserEventstore - View *view.View + UserEvents *user_event.UserEventstore + ProjectEvents *proj_event.ProjectEventstore + View *view.View } func (repo *TokenRepo) IsTokenValid(ctx context.Context, userID, tokenID string) (bool, error) { diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index 66248090fa..dbb0bf903d 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -5,6 +5,7 @@ import ( "github.com/caos/zitadel/internal/config/systemdefaults" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" + key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/logging" @@ -14,6 +15,7 @@ import ( "github.com/caos/zitadel/internal/eventstore" es_models "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/sdk" + key_view_model "github.com/caos/zitadel/internal/key/repository/view/model" org_model "github.com/caos/zitadel/internal/org/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/telemetry/tracing" @@ -252,10 +254,10 @@ func checkIDs(ctx context.Context, obj es_models.ObjectRoot) error { return nil } -func (repo *UserRepo) MachineKeyByID(ctx context.Context, keyID string) (*model.MachineKeyView, error) { - key, err := repo.View.MachineKeyByID(keyID) +func (repo *UserRepo) MachineKeyByID(ctx context.Context, keyID string) (*key_model.AuthNKeyView, error) { + key, err := repo.View.AuthNKeyByID(keyID) if err != nil { return nil, err } - return usr_view_model.MachineKeyToModel(key), nil + return key_view_model.AuthNKeyToModel(key), nil } diff --git a/internal/auth/repository/eventsourcing/eventstore/user_grant.go b/internal/auth/repository/eventsourcing/eventstore/user_grant.go index cba39e4314..e2bbd63703 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/auth/repository/eventsourcing/eventstore/user_grant.go @@ -2,7 +2,6 @@ package eventstore import ( "context" - "github.com/caos/logging" "github.com/caos/zitadel/internal/api/authz" @@ -39,7 +38,7 @@ func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *gran result := &grant_model.UserGrantSearchResponse{ Offset: request.Offset, Limit: request.Limit, - TotalResult: uint64(count), + TotalResult: count, Result: model.UserGrantsToModel(grants), } if err == nil { @@ -94,7 +93,43 @@ func membershipsToOrgResp(memberships []*user_view_model.UserMembershipView, cou } } +func (repo *UserGrantRepo) SearchMyUserMemberships(ctx context.Context, request *user_model.UserMembershipSearchRequest) (*user_model.UserMembershipSearchResponse, error) { + request.EnsureLimit(repo.SearchLimit) + sequence, sequenceErr := repo.View.GetLatestUserMembershipSequence() + logging.Log("EVENT-Dn7sf").OnError(sequenceErr).Warn("could not read latest user sequence") + + memberships, count, err := repo.View.SearchUserMemberships(request) + if err != nil { + return nil, err + } + result := &user_model.UserMembershipSearchResponse{ + Offset: request.Offset, + Limit: request.Limit, + TotalResult: count, + Result: user_view_model.UserMembershipsToModel(memberships), + } + if sequenceErr == nil { + result.Sequence = sequence.CurrentSequence + result.Timestamp = sequence.LastSuccessfulSpoolerRun + } + return result, nil +} + func (repo *UserGrantRepo) SearchMyZitadelPermissions(ctx context.Context) ([]string, error) { + memberships, err := repo.searchUserMemberships(ctx) + if err != nil { + return nil, err + } + permissions := &grant_model.Permissions{Permissions: []string{}} + for _, membership := range memberships { + for _, role := range membership.Roles { + permissions = repo.mapRoleToPermission(permissions, membership, role) + } + } + return permissions.Permissions, nil +} + +func (repo *UserGrantRepo) searchUserMemberships(ctx context.Context) ([]*user_view_model.UserMembershipView, error) { ctxData := authz.GetCtxData(ctx) orgMemberships, orgCount, err := repo.View.SearchUserMemberships(&user_model.UserMembershipSearchRequest{ Queries: []*user_model.UserMembershipSearchQuery{ @@ -131,16 +166,9 @@ func (repo *UserGrantRepo) SearchMyZitadelPermissions(ctx context.Context) ([]st return nil, err } if orgCount == 0 && iamCount == 0 { - return []string{}, nil + return []*user_view_model.UserMembershipView{}, nil } - orgMemberships = append(orgMemberships, iamMemberships...) - permissions := &grant_model.Permissions{Permissions: []string{}} - for _, membership := range orgMemberships { - for _, role := range membership.Roles { - permissions = repo.mapRoleToPermission(permissions, membership, role) - } - } - return permissions.Permissions, nil + return append(orgMemberships, iamMemberships...), nil } func (repo *UserGrantRepo) SearchMyProjectPermissions(ctx context.Context) ([]string, error) { @@ -258,7 +286,7 @@ func grantRespToOrgResp(grants *grant_model.UserGrantSearchResponse) *grant_mode func orgRespToOrgResp(orgs []*org_view_model.OrgView, count uint64) *grant_model.ProjectOrgSearchResponse { resp := &grant_model.ProjectOrgSearchResponse{ - TotalResult: uint64(count), + TotalResult: count, } resp.Result = make([]*grant_model.Org, len(orgs)) for i, o := range orgs { @@ -267,33 +295,6 @@ func orgRespToOrgResp(orgs []*org_view_model.OrgView, count uint64) *grant_model return resp } -func mergeOrgAndAdminGrant(ctxData authz.CtxData, orgGrant, iamAdminGrant *model.UserGrantView) (grant *authz.Grant) { - if orgGrant != nil { - roles := orgGrant.RoleKeys - if iamAdminGrant != nil { - roles = addIamAdminRoles(roles, iamAdminGrant.RoleKeys) - } - grant = &authz.Grant{OrgID: orgGrant.ResourceOwner, Roles: roles} - } else if iamAdminGrant != nil { - grant = &authz.Grant{ - OrgID: ctxData.OrgID, - Roles: iamAdminGrant.RoleKeys, - } - } - return grant -} - -func addIamAdminRoles(orgRoles, iamAdminRoles []string) []string { - result := make([]string, 0) - result = append(result, iamAdminRoles...) - for _, role := range orgRoles { - if !authz.ExistsPerm(result, role) { - result = append(result, role) - } - } - return result -} - func containsOrg(orgs []*grant_model.Org, resourceOwner string) bool { for _, org := range orgs { if org.OrgID == resourceOwner { @@ -302,3 +303,20 @@ func containsOrg(orgs []*grant_model.Org, resourceOwner string) bool { } return false } + +func userMembershipToMembership(membership *user_view_model.UserMembershipView) *authz.Membership { + return &authz.Membership{ + MemberType: authz.MemberType(membership.MemberType), + AggregateID: membership.AggregateID, + ObjectID: membership.ObjectID, + Roles: membership.Roles, + } +} + +func userMembershipsToMemberships(memberships []*user_view_model.UserMembershipView) []*authz.Membership { + result := make([]*authz.Membership, len(memberships)) + for i, m := range memberships { + result[i] = userMembershipToMembership(m) + } + return result +} diff --git a/internal/auth/repository/eventsourcing/handler/application.go b/internal/auth/repository/eventsourcing/handler/application.go index f8bd45d33a..49fa802825 100644 --- a/internal/auth/repository/eventsourcing/handler/application.go +++ b/internal/auth/repository/eventsourcing/handler/application.go @@ -84,6 +84,8 @@ func (a *Application) Reduce(event *models.Event) (err error) { case es_model.ApplicationChanged, es_model.OIDCConfigAdded, es_model.OIDCConfigChanged, + es_model.APIConfigAdded, + es_model.APIConfigChanged, es_model.ApplicationDeactivated, es_model.ApplicationReactivated: err = app.SetData(event) diff --git a/internal/auth/repository/eventsourcing/handler/authn_keys.go b/internal/auth/repository/eventsourcing/handler/authn_keys.go new file mode 100644 index 0000000000..0300b6b1b3 --- /dev/null +++ b/internal/auth/repository/eventsourcing/handler/authn_keys.go @@ -0,0 +1,120 @@ +package handler + +import ( + "time" + + "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" + key_model "github.com/caos/zitadel/internal/key/repository/view/model" + proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" + user_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" +) + +const ( + authnKeysTable = "auth.authn_keys" +) + +type AuthNKeys struct { + handler + subscription *eventstore.Subscription +} + +func newAuthNKeys(handler handler) *AuthNKeys { + h := &AuthNKeys{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *AuthNKeys) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + +func (k *AuthNKeys) ViewModel() string { + return authnKeysTable +} + +func (_ *AuthNKeys) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{user_model.UserAggregate, proj_model.ProjectAggregate} +} + +func (k *AuthNKeys) CurrentSequence() (uint64, error) { + sequence, err := k.view.GetLatestAuthNKeySequence() + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (k *AuthNKeys) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := k.view.GetLatestAuthNKeySequence() + if err != nil { + return nil, err + } + return es_models.NewSearchQuery(). + AggregateTypeFilter(k.AggregateTypes()...). + LatestSequenceFilter(sequence.CurrentSequence), nil +} + +func (k *AuthNKeys) Reduce(event *es_models.Event) (err error) { + switch event.AggregateType { + case user_model.UserAggregate, + proj_model.ProjectAggregate: + err = k.processAuthNKeys(event) + } + return err +} + +func (k *AuthNKeys) processAuthNKeys(event *es_models.Event) (err error) { + key := new(key_model.AuthNKeyView) + switch event.Type { + case user_model.MachineKeyAdded, + proj_model.ClientKeyAdded: + err = key.AppendEvent(event) + if key.ExpirationDate.Before(time.Now()) { + return k.view.ProcessedAuthNKeySequence(event) + } + case user_model.MachineKeyRemoved: + err = key.SetUserData(event) + if err != nil { + return err + } + return k.view.DeleteAuthNKey(key.ID, event) + case proj_model.ClientKeyRemoved: + err = key.SetClientData(event) + if err != nil { + return err + } + return k.view.DeleteAuthNKey(key.ID, event) + case user_model.UserRemoved, + proj_model.ApplicationRemoved: + return k.view.DeleteAuthNKeysByObjectID(event.AggregateID, event) + default: + return k.view.ProcessedAuthNKeySequence(event) + } + if err != nil { + return err + } + return k.view.PutAuthNKey(key, event) +} + +func (k *AuthNKeys) OnError(event *es_models.Event, err error) error { + logging.LogWithFields("SPOOL-S9fe", "id", event.AggregateID).WithError(err).Warn("something went wrong in authn key handler") + return spooler.HandleError(event, err, k.view.GetLatestAuthNKeyFailedEvent, k.view.ProcessedAuthNKeyFailedEvent, k.view.ProcessedAuthNKeySequence, k.errorCountUntilSkip) +} + +func (k *AuthNKeys) OnSuccess() error { + return spooler.HandleSuccess(k.view.UpdateAuthNKeySpoolerRunTimestamp) +} diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index c5825c5abe..b22983587d 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -71,7 +71,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es repos.OrgEvents, repos.IamEvents, systemDefaults.IamID), - newMachineKeys( + newAuthNKeys( handler{view, bulkLimit, configs.cycleDuration("MachineKey"), errorCount, es}), newLoginPolicy( handler{view, bulkLimit, configs.cycleDuration("LoginPolicy"), errorCount, es}), diff --git a/internal/auth/repository/eventsourcing/handler/machine_keys.go b/internal/auth/repository/eventsourcing/handler/machine_keys.go deleted file mode 100644 index bf877564af..0000000000 --- a/internal/auth/repository/eventsourcing/handler/machine_keys.go +++ /dev/null @@ -1,110 +0,0 @@ -package handler - -import ( - "time" - - "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/user/repository/eventsourcing/model" - usr_model "github.com/caos/zitadel/internal/user/repository/view/model" -) - -const ( - machineKeysTable = "auth.machine_keys" -) - -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 (_ *MachineKeys) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{model.UserAggregate} -} - -func (k *MachineKeys) CurrentSequence() (uint64, error) { - sequence, err := k.view.GetLatestMachineKeySequence() - 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(k.AggregateTypes()...). - LatestSequenceFilter(sequence.CurrentSequence), nil -} - -func (k *MachineKeys) Reduce(event *es_models.Event) (err error) { - switch event.AggregateType { - case model.UserAggregate: - err = k.processMachineKeys(event) - } - return err -} - -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 k.view.ProcessedMachineKeySequence(event) - } - case model.MachineKeyRemoved: - err = key.SetData(event) - if err != nil { - return err - } - return k.view.DeleteMachineKey(key.ID, event) - case model.UserRemoved: - return k.view.DeleteMachineKeysByUserID(event.AggregateID, event) - default: - return k.view.ProcessedMachineKeySequence(event) - } - if err != nil { - return err - } - return k.view.PutMachineKey(key, event) -} - -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, k.view.GetLatestMachineKeyFailedEvent, k.view.ProcessedMachineKeyFailedEvent, k.view.ProcessedMachineKeySequence, k.errorCountUntilSkip) -} - -func (k *MachineKeys) OnSuccess() error { - return spooler.HandleSuccess(k.view.UpdateMachineKeySpoolerRunTimestamp) -} diff --git a/internal/auth/repository/eventsourcing/handler/token.go b/internal/auth/repository/eventsourcing/handler/token.go index 6a365580ac..1b28d5fd1d 100644 --- a/internal/auth/repository/eventsourcing/handler/token.go +++ b/internal/auth/repository/eventsourcing/handler/token.go @@ -5,6 +5,7 @@ import ( "encoding/json" "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" @@ -71,9 +72,6 @@ func (t *Token) EventQuery() (*models.SearchQuery, error) { if err != nil { return nil, err } - if err != nil { - return nil, err - } return es_models.NewSearchQuery(). AggregateTypeFilter(user_es_model.UserAggregate, project_es_model.ProjectAggregate). LatestSequenceFilter(sequence.CurrentSequence), nil diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go index c8622e24cb..48b8a46966 100644 --- a/internal/auth/repository/eventsourcing/repository.go +++ b/internal/auth/repository/eventsourcing/repository.go @@ -2,6 +2,7 @@ package eventsourcing import ( "context" + "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/eventstore" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/handler" @@ -148,8 +149,9 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co IAMID: systemDefaults.IamID, }, eventstore.TokenRepo{ - UserEvents: user, - View: view, + UserEvents: user, + ProjectEvents: project, + View: view, }, eventstore.KeyRepository{ View: view, diff --git a/internal/auth/repository/eventsourcing/view/authn_keys.go b/internal/auth/repository/eventsourcing/view/authn_keys.go new file mode 100644 index 0000000000..4873c64786 --- /dev/null +++ b/internal/auth/repository/eventsourcing/view/authn_keys.go @@ -0,0 +1,74 @@ +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" +) + +const ( + authNKeyTable = "auth.authn_keys" +) + +func (v *View) AuthNKeyByIDs(userID, keyID string) (*model.AuthNKeyView, error) { + return view.AuthNKeyByIDs(v.Db, authNKeyTable, userID, keyID) +} + +func (v *View) AuthNKeysByObjectID(objectID string) ([]*model.AuthNKeyView, error) { + return view.AuthNKeysByObjectID(v.Db, authNKeyTable, objectID) +} + +func (v *View) AuthNKeyByID(keyID string) (*model.AuthNKeyView, error) { + return view.AuthNKeyByID(v.Db, authNKeyTable, keyID) +} + +func (v *View) SearchAuthNKeys(request *key_model.AuthNKeySearchRequest) ([]*model.AuthNKeyView, uint64, error) { + return view.SearchAuthNKeys(v.Db, authNKeyTable, request) +} + +func (v *View) PutAuthNKey(key *model.AuthNKeyView, event *models.Event) error { + err := view.PutAuthNKey(v.Db, authNKeyTable, key) + if err != nil { + return err + } + return v.ProcessedAuthNKeySequence(event) +} + +func (v *View) DeleteAuthNKey(keyID string, event *models.Event) error { + err := view.DeleteAuthNKey(v.Db, authNKeyTable, keyID) + if err != nil && !errors.IsNotFound(err) { + return err + } + return v.ProcessedAuthNKeySequence(event) +} + +func (v *View) DeleteAuthNKeysByObjectID(objectID string, event *models.Event) error { + err := view.DeleteAuthNKey(v.Db, authNKeyTable, objectID) + if err != nil && !errors.IsNotFound(err) { + return err + } + return v.ProcessedAuthNKeySequence(event) +} + +func (v *View) GetLatestAuthNKeySequence() (*repository.CurrentSequence, error) { + return v.latestSequence(authNKeyTable) +} + +func (v *View) ProcessedAuthNKeySequence(event *models.Event) error { + return v.saveCurrentSequence(authNKeyTable, event) +} + +func (v *View) UpdateAuthNKeySpoolerRunTimestamp() error { + return v.updateSpoolerRunSequence(authNKeyTable) +} + +func (v *View) GetLatestAuthNKeyFailedEvent(sequence uint64) (*repository.FailedEvent, error) { + return v.latestFailedEvent(authNKeyTable, sequence) +} + +func (v *View) ProcessedAuthNKeyFailedEvent(failedEvent *repository.FailedEvent) error { + return v.saveFailedEvent(failedEvent) +} diff --git a/internal/auth/repository/eventsourcing/view/machine_keys.go b/internal/auth/repository/eventsourcing/view/machine_keys.go deleted file mode 100644 index 908dc54d8c..0000000000 --- a/internal/auth/repository/eventsourcing/view/machine_keys.go +++ /dev/null @@ -1,74 +0,0 @@ -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" -) - -const ( - machineKeyTable = "auth.machine_keys" -) - -func (v *View) MachineKeyByIDs(userID, keyID string) (*model.MachineKeyView, error) { - return view.MachineKeyByIDs(v.Db, machineKeyTable, userID, keyID) -} - -func (v *View) MachineKeysByUserID(userID string) ([]*model.MachineKeyView, error) { - return view.MachineKeysByUserID(v.Db, machineKeyTable, userID) -} - -func (v *View) MachineKeyByID(keyID string) (*model.MachineKeyView, error) { - return view.MachineKeyByID(v.Db, machineKeyTable, keyID) -} - -func (v *View) SearchMachineKeys(request *usr_model.MachineKeySearchRequest) ([]*model.MachineKeyView, uint64, error) { - return view.SearchMachineKeys(v.Db, machineKeyTable, request) -} - -func (v *View) PutMachineKey(key *model.MachineKeyView, event *models.Event) error { - err := view.PutMachineKey(v.Db, machineKeyTable, key) - if err != nil { - return err - } - return v.ProcessedMachineKeySequence(event) -} - -func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { - err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedMachineKeySequence(event) -} - -func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { - err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedMachineKeySequence(event) -} - -func (v *View) GetLatestMachineKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(machineKeyTable) -} - -func (v *View) ProcessedMachineKeySequence(event *models.Event) error { - return v.saveCurrentSequence(machineKeyTable, event) -} - -func (v *View) UpdateMachineKeySpoolerRunTimestamp() error { - return v.updateSpoolerRunSequence(machineKeyTable) -} - -func (v *View) GetLatestMachineKeyFailedEvent(sequence uint64) (*repository.FailedEvent, error) { - return v.latestFailedEvent(machineKeyTable, sequence) -} - -func (v *View) ProcessedMachineKeyFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} diff --git a/internal/auth/repository/org.go b/internal/auth/repository/org.go index cb38bcf872..878d4756a6 100644 --- a/internal/auth/repository/org.go +++ b/internal/auth/repository/org.go @@ -3,9 +3,11 @@ package repository import ( "context" iam_model "github.com/caos/zitadel/internal/iam/model" + org_model "github.com/caos/zitadel/internal/org/model" ) type OrgRepository interface { + OrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) GetOrgIAMPolicy(ctx context.Context, orgID string) (*iam_model.OrgIAMPolicyView, error) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) GetIDPConfigByID(ctx context.Context, idpConfigID string) (*iam_model.IDPConfigView, error) diff --git a/internal/auth/repository/user.go b/internal/auth/repository/user.go index 46a93c13f1..90bad714e4 100644 --- a/internal/auth/repository/user.go +++ b/internal/auth/repository/user.go @@ -3,6 +3,8 @@ package repository import ( "context" + key_model "github.com/caos/zitadel/internal/key/model" + "github.com/caos/zitadel/internal/user/model" ) @@ -16,7 +18,7 @@ type UserRepository interface { UserByID(ctx context.Context, userID string) (*model.UserView, error) UserByLoginName(ctx context.Context, loginName string) (*model.UserView, error) - MachineKeyByID(ctx context.Context, keyID string) (*model.MachineKeyView, error) + MachineKeyByID(ctx context.Context, keyID string) (*key_model.AuthNKeyView, error) } type myUserRepo interface { @@ -37,4 +39,6 @@ type myUserRepo interface { GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error) + + SearchMyUserMemberships(ctx context.Context, request *model.UserMembershipSearchRequest) (*model.UserMembershipSearchResponse, error) } diff --git a/internal/authz/repository/eventsourcing/eventstore/user_grant.go b/internal/authz/repository/eventsourcing/eventstore/user_grant.go index 6028f3e49a..28370187ac 100644 --- a/internal/authz/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/authz/repository/eventsourcing/eventstore/user_grant.go @@ -7,8 +7,10 @@ import ( "github.com/caos/zitadel/internal/authz/repository/eventsourcing/view" caos_errs "github.com/caos/zitadel/internal/errors" iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing" + global_model "github.com/caos/zitadel/internal/model" + user_model "github.com/caos/zitadel/internal/user/model" + user_view_model "github.com/caos/zitadel/internal/user/repository/view/model" grant_model "github.com/caos/zitadel/internal/usergrant/model" - "github.com/caos/zitadel/internal/usergrant/repository/view/model" "github.com/caos/zitadel/internal/v2/domain" ) @@ -24,46 +26,70 @@ func (repo *UserGrantRepo) Health() error { return repo.View.Health() } -func (repo *UserGrantRepo) ResolveGrants(ctx context.Context) (*authz.Grant, error) { - err := repo.FillIamProjectID(ctx) +func (repo *UserGrantRepo) SearchMyMemberships(ctx context.Context) ([]*authz.Membership, error) { + memberships, err := repo.searchUserMemberships(ctx) if err != nil { return nil, err } - ctxData := authz.GetCtxData(ctx) - - orgGrant, err := repo.View.UserGrantByIDs(ctxData.OrgID, repo.IamProjectID, ctxData.UserID) - if err != nil && !caos_errs.IsNotFound(err) { - return nil, err - } - iamAdminGrant, err := repo.View.UserGrantByIDs(repo.IamID, repo.IamProjectID, ctxData.UserID) - if err != nil && !caos_errs.IsNotFound(err) { - return nil, err - } - - return mergeOrgAndAdminGrant(ctxData, orgGrant, iamAdminGrant), nil + return userMembershipsToMemberships(memberships), nil } func (repo *UserGrantRepo) SearchMyZitadelPermissions(ctx context.Context) ([]string, error) { - grant, err := repo.ResolveGrants(ctx) + memberships, err := repo.searchUserMemberships(ctx) if err != nil { return nil, err } - - if grant == nil { - return []string{}, nil - } permissions := &grant_model.Permissions{Permissions: []string{}} - for _, role := range grant.Roles { - roleName, ctxID := authz.SplitPermission(role) - for _, mapping := range repo.Auth.RolePermissionMappings { - if mapping.Role == roleName { - permissions.AppendPermissions(ctxID, mapping.Permissions...) - } + for _, membership := range memberships { + for _, role := range membership.Roles { + permissions = repo.mapRoleToPermission(permissions, membership, role) } } return permissions.Permissions, nil } +func (repo *UserGrantRepo) searchUserMemberships(ctx context.Context) ([]*user_view_model.UserMembershipView, error) { + ctxData := authz.GetCtxData(ctx) + orgMemberships, orgCount, err := repo.View.SearchUserMemberships(&user_model.UserMembershipSearchRequest{ + Queries: []*user_model.UserMembershipSearchQuery{ + { + Key: user_model.UserMembershipSearchKeyUserID, + Method: global_model.SearchMethodEquals, + Value: ctxData.UserID, + }, + { + Key: user_model.UserMembershipSearchKeyResourceOwner, + Method: global_model.SearchMethodEquals, + Value: ctxData.OrgID, + }, + }, + }) + if err != nil { + return nil, err + } + iamMemberships, iamCount, err := repo.View.SearchUserMemberships(&user_model.UserMembershipSearchRequest{ + Queries: []*user_model.UserMembershipSearchQuery{ + { + Key: user_model.UserMembershipSearchKeyUserID, + Method: global_model.SearchMethodEquals, + Value: ctxData.UserID, + }, + { + Key: user_model.UserMembershipSearchKeyAggregateID, + Method: global_model.SearchMethodEquals, + Value: repo.IamID, + }, + }, + }) + if err != nil { + return nil, err + } + if orgCount == 0 && iamCount == 0 { + return []*user_view_model.UserMembershipView{}, nil + } + return append(orgMemberships, iamMemberships...), nil +} + func (repo *UserGrantRepo) FillIamProjectID(ctx context.Context) error { if repo.IamProjectID != "" { return nil @@ -79,29 +105,32 @@ func (repo *UserGrantRepo) FillIamProjectID(ctx context.Context) error { return nil } -func mergeOrgAndAdminGrant(ctxData authz.CtxData, orgGrant, iamAdminGrant *model.UserGrantView) (grant *authz.Grant) { - if orgGrant != nil { - roles := orgGrant.RoleKeys - if iamAdminGrant != nil { - roles = addIamAdminRoles(roles, iamAdminGrant.RoleKeys) - } - grant = &authz.Grant{OrgID: orgGrant.ResourceOwner, Roles: roles} - } else if iamAdminGrant != nil { - grant = &authz.Grant{ - OrgID: ctxData.OrgID, - Roles: iamAdminGrant.RoleKeys, +func (repo *UserGrantRepo) mapRoleToPermission(permissions *grant_model.Permissions, membership *user_view_model.UserMembershipView, role string) *grant_model.Permissions { + for _, mapping := range repo.Auth.RolePermissionMappings { + if mapping.Role == role { + ctxID := "" + if membership.MemberType == int32(user_model.MemberTypeProject) || membership.MemberType == int32(user_model.MemberTypeProjectGrant) { + ctxID = membership.ObjectID + } + permissions.AppendPermissions(ctxID, mapping.Permissions...) } } - return grant + return permissions } -func addIamAdminRoles(orgRoles, iamAdminRoles []string) []string { - result := make([]string, 0) - result = append(result, iamAdminRoles...) - for _, role := range orgRoles { - if !authz.ExistsPerm(result, role) { - result = append(result, role) - } +func userMembershipToMembership(membership *user_view_model.UserMembershipView) *authz.Membership { + return &authz.Membership{ + MemberType: authz.MemberType(membership.MemberType), + AggregateID: membership.AggregateID, + ObjectID: membership.ObjectID, + Roles: membership.Roles, + } +} + +func userMembershipsToMemberships(memberships []*user_view_model.UserMembershipView) []*authz.Membership { + result := make([]*authz.Membership, len(memberships)) + for i, m := range memberships { + result[i] = userMembershipToMembership(m) } return result } diff --git a/internal/authz/repository/eventsourcing/handler/handler.go b/internal/authz/repository/eventsourcing/handler/handler.go index ee21d1b020..b18098de16 100644 --- a/internal/authz/repository/eventsourcing/handler/handler.go +++ b/internal/authz/repository/eventsourcing/handler/handler.go @@ -1,6 +1,7 @@ package handler import ( + "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "time" "github.com/caos/zitadel/internal/authz/repository/eventsourcing/view" @@ -8,7 +9,8 @@ import ( "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" + org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" + project_events "github.com/caos/zitadel/internal/project/repository/eventsourcing" ) type Configs map[string]*Config @@ -31,15 +33,21 @@ func (h *handler) Eventstore() eventstore.Eventstore { } type EventstoreRepos struct { - IAMEvents *iam_events.IAMEventstore + IAMEvents *eventsourcing.IAMEventstore + OrgEvents *org_events.OrgEventstore + ProjectEvents *project_events.ProjectEventstore } func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []query.Handler { return []query.Handler{ newUserGrant( - handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, + handler{view, bulkLimit, configs.cycleDuration("UserGrants"), errorCount, es}, repos.IAMEvents, systemDefaults.IamID), + newUserMembership( + handler{view, bulkLimit, configs.cycleDuration("UserMemberships"), errorCount, es}, + repos.OrgEvents, + repos.ProjectEvents), newApplication( handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}), newOrg( diff --git a/internal/authz/repository/eventsourcing/handler/user_membership.go b/internal/authz/repository/eventsourcing/handler/user_membership.go new file mode 100644 index 0000000000..4e1b5a81cf --- /dev/null +++ b/internal/authz/repository/eventsourcing/handler/user_membership.go @@ -0,0 +1,274 @@ +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" + 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 = "authz.user_memberships" +) + +type UserMembership struct { + handler + orgEvents *org_event.OrgEventstore + projectEvents *proj_event.ProjectEventstore + subscription *eventstore.Subscription +} + +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() (uint64, error) { + sequence, err := m.view.GetLatestUserMembershipSequence() + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (m *UserMembership) EventQuery() (*models.SearchQuery, error) { + sequence, err := m.view.GetLatestUserMembershipSequence() + if err != nil { + return nil, err + } + return es_models.NewSearchQuery(). + 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) + case org_es_model.OrgAggregate: + err = m.processOrg(event) + case proj_es_model.ProjectAggregate: + err = m.processProject(event) + case model.UserAggregate: + err = m.processUser(event) + } + return err +} + +func (m *UserMembership) processIAM(event *models.Event) (err error) { + member := new(usr_es_model.UserMembershipView) + err = member.AppendEvent(event) + if err != nil { + return err + } + switch event.Type { + case iam_es_model.IAMMemberAdded: + m.fillIamDisplayName(member) + case iam_es_model.IAMMemberChanged: + member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam) + if err != nil { + return err + } + err = member.AppendEvent(event) + case iam_es_model.IAMMemberRemoved: + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) + default: + return m.view.ProcessedUserMembershipSequence(event) + } + if err != nil { + return err + } + return m.view.PutUserMembership(member, event) +} + +func (m *UserMembership) fillIamDisplayName(member *usr_es_model.UserMembershipView) { + member.DisplayName = member.AggregateID + member.ResourceOwnerName = member.ResourceOwner +} + +func (m *UserMembership) processOrg(event *models.Event) (err error) { + member := new(usr_es_model.UserMembershipView) + err = member.AppendEvent(event) + if err != nil { + return err + } + switch event.Type { + case org_es_model.OrgMemberAdded: + err = m.fillOrgName(member) + case org_es_model.OrgMemberChanged: + member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation) + if err != nil { + return err + } + err = member.AppendEvent(event) + case org_es_model.OrgMemberRemoved: + 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) + } + if err != nil { + return err + } + return m.view.PutUserMembership(member, event) +} + +func (m *UserMembership) fillOrgName(member *usr_es_model.UserMembershipView) (err error) { + org, err := m.orgEvents.OrgByID(context.Background(), org_model.NewOrg(member.ResourceOwner)) + if err != nil { + return err + } + member.ResourceOwnerName = org.Name + if member.AggregateID == org.AggregateID { + member.DisplayName = org.Name + } + return nil +} + +func (m *UserMembership) updateOrgName(event *models.Event) error { + org, err := m.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.AggregateID)) + if err != nil { + return err + } + + memberships, err := m.view.UserMembershipsByResourceOwner(event.ResourceOwner) + if err != nil { + return err + } + for _, membership := range memberships { + membership.ResourceOwnerName = org.Name + if membership.AggregateID == event.AggregateID { + membership.DisplayName = org.Name + } + } + return m.view.BulkPutUserMemberships(memberships, event) +} + +func (m *UserMembership) processProject(event *models.Event) (err error) { + member := new(usr_es_model.UserMembershipView) + err = member.AppendEvent(event) + if err != nil { + return err + } + switch event.Type { + case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectGrantMemberAdded: + err = m.fillProjectDisplayName(member) + if err != nil { + return err + } + err = m.fillOrgName(member) + case proj_es_model.ProjectMemberChanged: + member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject) + if err != nil { + return err + } + err = member.AppendEvent(event) + case proj_es_model.ProjectMemberRemoved: + 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 { + return err + } + err = member.AppendEvent(event) + case proj_es_model.ProjectGrantMemberRemoved: + 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) + case proj_es_model.ProjectGrantRemoved: + return m.view.DeleteUserMembershipsByAggregateIDAndObjectID(event.AggregateID, member.ObjectID, event) + default: + return m.view.ProcessedUserMembershipSequence(event) + } + if err != nil { + return err + } + return m.view.PutUserMembership(member, event) +} + +func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembershipView) (err error) { + project, err := m.projectEvents.ProjectByID(context.Background(), member.AggregateID) + if err != nil { + return err + } + member.DisplayName = project.Name + return nil +} + +func (m *UserMembership) updateProjectDisplayName(event *models.Event) error { + project, err := m.projectEvents.ProjectByID(context.Background(), event.AggregateID) + if err != nil { + return err + } + + memberships, err := m.view.UserMembershipsByAggregateID(event.AggregateID) + if err != nil { + return err + } + for _, membership := range memberships { + membership.DisplayName = project.Name + } + 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) + default: + return m.view.ProcessedUserMembershipSequence(event) + } +} + +func (m *UserMembership) OnError(event *models.Event, err error) error { + logging.LogWithFields("SPOOL-Ms3fj", "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) +} + +func (m *UserMembership) OnSuccess() error { + return spooler.HandleSuccess(m.view.UpdateUserMembershipSpoolerRunTimestamp) +} diff --git a/internal/authz/repository/eventsourcing/repository.go b/internal/authz/repository/eventsourcing/repository.go index 3a107c8348..73821aa71e 100644 --- a/internal/authz/repository/eventsourcing/repository.go +++ b/internal/authz/repository/eventsourcing/repository.go @@ -3,6 +3,7 @@ package eventsourcing import ( "context" "github.com/caos/zitadel/internal/v2/query" + es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing" es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing" @@ -22,6 +23,7 @@ import ( ) type Config struct { + Domain string Eventstore es_int.Config AuthRequest cache.Config View types.SQL @@ -60,6 +62,8 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults) (* if err != nil { return nil, err } + org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es, IAMDomain: conf.Domain}, systemDefaults) + project, err := es_proj.StartProject(es_proj.ProjectConfig{ Eventstore: es, Cache: conf.Eventstore.Cache, @@ -82,7 +86,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults) (* return nil, err } - repos := handler.EventstoreRepos{IAMEvents: iam} + repos := handler.EventstoreRepos{IAMEvents: iam, OrgEvents: org, ProjectEvents: project} spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, repos, systemDefaults) return &EsRepository{ diff --git a/internal/authz/repository/eventsourcing/view/user_membership.go b/internal/authz/repository/eventsourcing/view/user_membership.go new file mode 100644 index 0000000000..add7b3aa81 --- /dev/null +++ b/internal/authz/repository/eventsourcing/view/user_membership.go @@ -0,0 +1,98 @@ +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" +) + +const ( + userMembershipTable = "authz.user_memberships" +) + +func (v *View) UserMembershipByIDs(userID, aggregateID, objectID string, memberType usr_model.MemberType) (*model.UserMembershipView, error) { + return view.UserMembershipByIDs(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType) +} + +func (v *View) UserMembershipsByAggregateID(aggregateID string) ([]*model.UserMembershipView, error) { + return view.UserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) +} + +func (v *View) UserMembershipsByResourceOwner(resourceOwner string) ([]*model.UserMembershipView, error) { + return view.UserMembershipsByResourceOwner(v.Db, userMembershipTable, resourceOwner) +} + +func (v *View) SearchUserMemberships(request *usr_model.UserMembershipSearchRequest) ([]*model.UserMembershipView, uint64, error) { + return view.SearchUserMemberships(v.Db, userMembershipTable, request) +} + +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(event) +} + +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(event) +} + +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 && !errors.IsNotFound(err) { + return err + } + return v.ProcessedUserMembershipSequence(event) +} + +func (v *View) DeleteUserMembershipsByUserID(userID string, event *models.Event) error { + err := view.DeleteUserMembershipsByUserID(v.Db, userMembershipTable, userID) + if err != nil && !errors.IsNotFound(err) { + return err + } + return v.ProcessedUserMembershipSequence(event) +} + +func (v *View) DeleteUserMembershipsByAggregateID(aggregateID string, event *models.Event) error { + err := view.DeleteUserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) + if err != nil && !errors.IsNotFound(err) { + return err + } + return v.ProcessedUserMembershipSequence(event) +} + +func (v *View) DeleteUserMembershipsByAggregateIDAndObjectID(aggregateID, objectID string, event *models.Event) error { + err := view.DeleteUserMembershipsByAggregateIDAndObjectID(v.Db, userMembershipTable, aggregateID, objectID) + if err != nil && !errors.IsNotFound(err) { + return err + } + return v.ProcessedUserMembershipSequence(event) +} + +func (v *View) GetLatestUserMembershipSequence() (*repository.CurrentSequence, error) { + return v.latestSequence(userMembershipTable) +} + +func (v *View) ProcessedUserMembershipSequence(event *models.Event) error { + return v.saveCurrentSequence(userMembershipTable, event) +} + +func (v *View) UpdateUserMembershipSpoolerRunTimestamp() error { + return v.updateSpoolerRunSequence(userMembershipTable) +} + +func (v *View) GetLatestUserMembershipFailedEvent(sequence uint64) (*repository.FailedEvent, error) { + return v.latestFailedEvent(userMembershipTable, sequence) +} + +func (v *View) ProcessedUserMembershipFailedEvent(failedEvent *repository.FailedEvent) error { + return v.saveFailedEvent(failedEvent) +} diff --git a/internal/config/systemdefaults/system_defaults.go b/internal/config/systemdefaults/system_defaults.go index 8f47681ff2..b623aad376 100644 --- a/internal/config/systemdefaults/system_defaults.go +++ b/internal/config/systemdefaults/system_defaults.go @@ -40,6 +40,7 @@ type SecretGenerators struct { PhoneVerificationCode crypto.GeneratorConfig PasswordVerificationCode crypto.GeneratorConfig MachineKeySize uint32 + ClientKeySize uint32 } type MultifactorConfig struct { diff --git a/internal/iam/repository/eventsourcing/eventstore_test.go b/internal/iam/repository/eventsourcing/eventstore_test.go index e6c304ef62..a32efc0767 100644 --- a/internal/iam/repository/eventsourcing/eventstore_test.go +++ b/internal/iam/repository/eventsourcing/eventstore_test.go @@ -3061,3 +3061,321 @@ func TestChangeMailText(t *testing.T) { }) } } +func TestAddMailTemplate(t *testing.T) { + ctrl := gomock.NewController(t) + type args struct { + es *IAMEventstore + ctx context.Context + policy *iam_model.MailTemplate + } + type res struct { + result *iam_model.MailTemplate + wantErr bool + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "add mailtemplate, ok", + args: args{ + es: GetMockManipulateIAM(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + Template: []byte(""), + }, + }, + res: res{ + result: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + Template: []byte(""), + }, + }, + }, + { + name: "invalid policy", + args: args{ + es: GetMockManipulateIAM(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsPreconditionFailed, + }, + }, + { + name: "existing iam not found", + args: args{ + es: GetMockManipulateIAMNotExisting(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsNotFound, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := tt.args.es.AddMailTemplate(tt.args.ctx, tt.args.policy) + if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) { + t.Errorf("got wrong err: %v ", err) + return + } + if tt.res.wantErr && tt.res.errFunc(err) { + return + } + if string(result.Template) != string(tt.res.result.Template) { + t.Errorf("got wrong result Template: expected: %v, actual: %v ", tt.res.result.Template, result.Template) + } + }) + } +} + +func TestChangeMailTemplate(t *testing.T) { + ctrl := gomock.NewController(t) + type args struct { + es *IAMEventstore + ctx context.Context + template *iam_model.MailTemplate + } + type res struct { + result *iam_model.MailTemplate + wantErr bool + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "add mail template, ok", + args: args{ + es: GetMockManipulateIAMWithMailTemplate(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + template: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + Template: []byte(""), + }, + }, + res: res{ + result: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + Template: []byte(""), + }, + }, + }, + { + name: "invalid mail template", + args: args{ + es: GetMockManipulateIAM(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + template: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsPreconditionFailed, + }, + }, + { + name: "existing iam not found", + args: args{ + es: GetMockManipulateIAMNotExisting(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + template: &iam_model.MailTemplate{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsNotFound, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := tt.args.es.ChangeMailTemplate(tt.args.ctx, tt.args.template) + if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) { + t.Errorf("got wrong err: %v ", err) + return + } + if tt.res.wantErr && tt.res.errFunc(err) { + return + } + if string(result.Template) != string(tt.res.result.Template) { + t.Errorf("got wrong result Template: expected: %v, actual: %v ", tt.res.result.Template, result.Template) + } + }) + } +} +func TestAddMailText(t *testing.T) { + ctrl := gomock.NewController(t) + type args struct { + es *IAMEventstore + ctx context.Context + policy *iam_model.MailText + } + type res struct { + result *iam_model.MailText + wantErr bool + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "add mailtemplate, ok", + args: args{ + es: GetMockManipulateIAM(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + MailTextType: "Type", Language: "DE", + }, + }, + res: res{ + result: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + MailTextType: "Type", Language: "DE", + }, + }, + }, + { + name: "invalid policy", + args: args{ + es: GetMockManipulateIAM(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsPreconditionFailed, + }, + }, + { + name: "existing iam not found", + args: args{ + es: GetMockManipulateIAMNotExisting(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsNotFound, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := tt.args.es.AddMailText(tt.args.ctx, tt.args.policy) + if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) { + t.Errorf("got wrong err: %v ", err) + return + } + if tt.res.wantErr && tt.res.errFunc(err) { + return + } + if string(result.MailTextType) != string(tt.res.result.MailTextType) { + t.Errorf("got wrong result MailTextType: expected: %v, actual: %v ", tt.res.result.MailTextType, result.MailTextType) + } + }) + } +} + +func TestChangeMailText(t *testing.T) { + ctrl := gomock.NewController(t) + type args struct { + es *IAMEventstore + ctx context.Context + policy *iam_model.MailText + } + type res struct { + result *iam_model.MailText + wantErr bool + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "change mailtemplate, ok", + args: args{ + es: GetMockManipulateIAMWithMailText(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + MailTextType: "Type", Language: "DE", + }, + }, + res: res{ + result: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + MailTextType: "Type", Language: "DE", + }, + }, + }, + { + name: "invalid policy", + args: args{ + es: GetMockManipulateIAM(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsPreconditionFailed, + }, + }, + { + name: "existing iam not found", + args: args{ + es: GetMockManipulateIAMNotExisting(ctrl), + ctx: authz.NewMockContext("orgID", "userID"), + policy: &iam_model.MailText{ + ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0}, + }, + }, + res: res{ + wantErr: true, + errFunc: caos_errs.IsNotFound, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := tt.args.es.ChangeMailText(tt.args.ctx, tt.args.policy) + if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) { + t.Errorf("got wrong err: %v ", err) + return + } + if tt.res.wantErr && tt.res.errFunc(err) { + return + } + if string(result.MailTextType) != string(tt.res.result.MailTextType) { + t.Errorf("got wrong result MailTextType: expected: %v, actual: %v ", tt.res.result.MailTextType, result.MailTextType) + } + }) + } +} diff --git a/internal/iam/repository/eventsourcing/model/iam.go b/internal/iam/repository/eventsourcing/model/iam.go index 23478dfdbc..f67bb55edb 100644 --- a/internal/iam/repository/eventsourcing/model/iam.go +++ b/internal/iam/repository/eventsourcing/model/iam.go @@ -83,13 +83,13 @@ func IAMToModel(iam *IAM) *model.IAM { idps := IDPConfigsToModel(iam.IDPs) mailTexts := MailTextsToModel(iam.DefaultMailTexts) converted := &model.IAM{ - ObjectRoot: iam.ObjectRoot, - SetUpStarted: domain.Step(iam.SetUpStarted), - SetUpDone: domain.Step(iam.SetUpDone), - GlobalOrgID: iam.GlobalOrgID, - IAMProjectID: iam.IAMProjectID, - Members: members, - IDPs: idps, + ObjectRoot: iam.ObjectRoot, + SetUpStarted: domain.Step(iam.SetUpStarted), + SetUpDone: domain.Step(iam.SetUpDone), + GlobalOrgID: iam.GlobalOrgID, + IAMProjectID: iam.IAMProjectID, + Members: members, + IDPs: idps, DefaultMailTexts: mailTexts, } if iam.DefaultLoginPolicy != nil { diff --git a/internal/key/model/authn_key.go b/internal/key/model/authn_key.go new file mode 100644 index 0000000000..417c812a3e --- /dev/null +++ b/internal/key/model/authn_key.go @@ -0,0 +1,101 @@ +package model + +import ( + "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/model" +) + +const ( + yearLayout = "2006-01-02" + defaultExpirationDate = "9999-01-01" +) + +type AuthNKeyView struct { + ID string + ObjectID string + ObjectType ObjectType + AuthIdentifier string + Type AuthNKeyType + Sequence uint64 + CreationDate time.Time + ExpirationDate time.Time + PublicKey []byte + State AuthNKeyState +} + +type AuthNKey struct { + models.ObjectRoot + + KeyID string + ObjectType ObjectType + Type AuthNKeyType + ExpirationDate time.Time + PrivateKey []byte +} + +type AuthNKeyType int32 + +const ( + AuthNKeyTypeNONE = iota + AuthNKeyTypeJSON +) + +type AuthNKeyState int32 + +const ( + AuthNKeyStateActive AuthNKeyState = iota + AuthNKeyStateInactive + AuthNKeyStateRemoved +) + +type AuthNKeySearchRequest struct { + Offset uint64 + Limit uint64 + SortingColumn AuthNKeySearchKey + Asc bool + Queries []*AuthNKeySearchQuery +} + +type AuthNKeySearchKey int32 + +const ( + AuthNKeyKeyUnspecified AuthNKeySearchKey = iota + AuthNKeyKeyID + AuthNKeyObjectID + AuthNKeyObjectType +) + +type ObjectType int32 + +const ( + AuthNKeyObjectTypeUnspecified ObjectType = iota + AuthNKeyObjectTypeUser + AuthNKeyObjectTypeApplication +) + +type AuthNKeySearchQuery struct { + Key AuthNKeySearchKey + Method model.SearchMethod + Value interface{} +} + +type AuthNKeySearchResponse struct { + Offset uint64 + Limit uint64 + TotalResult uint64 + Result []*AuthNKeyView + Sequence uint64 + Timestamp time.Time +} + +func (r *AuthNKeySearchRequest) EnsureLimit(limit uint64) { + if r.Limit == 0 || r.Limit > limit { + r.Limit = limit + } +} + +func DefaultExpiration() (time.Time, error) { + return time.Parse(yearLayout, defaultExpirationDate) +} diff --git a/internal/key/repository/view/authn_key_view.go b/internal/key/repository/view/authn_key_view.go new file mode 100644 index 0000000000..5bd299150e --- /dev/null +++ b/internal/key/repository/view/authn_key_view.go @@ -0,0 +1,77 @@ +package view + +import ( + caos_errs "github.com/caos/zitadel/internal/errors" + key_model "github.com/caos/zitadel/internal/key/model" + "github.com/caos/zitadel/internal/key/repository/view/model" + global_model "github.com/caos/zitadel/internal/model" + "github.com/caos/zitadel/internal/view/repository" + "github.com/jinzhu/gorm" +) + +func AuthNKeyByIDs(db *gorm.DB, table, objectID, keyID string) (*model.AuthNKeyView, error) { + key := new(model.AuthNKeyView) + query := repository.PrepareGetByQuery(table, + model.AuthNKeySearchQuery{Key: key_model.AuthNKeyObjectID, Method: global_model.SearchMethodEquals, Value: objectID}, + model.AuthNKeySearchQuery{Key: key_model.AuthNKeyKeyID, Method: global_model.SearchMethodEquals, Value: keyID}, + ) + err := query(db, key) + if caos_errs.IsNotFound(err) { + return nil, caos_errs.ThrowNotFound(nil, "VIEW-3Dk9s", "Errors.User.KeyNotFound") + } + return key, err +} + +func SearchAuthNKeys(db *gorm.DB, table string, req *key_model.AuthNKeySearchRequest) ([]*model.AuthNKeyView, uint64, error) { + keys := make([]*model.AuthNKeyView, 0) + query := repository.PrepareSearchQuery(table, model.AuthNKeySearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries}) + count, err := query(db, &keys) + if err != nil { + return nil, 0, err + } + return keys, count, nil +} + +func AuthNKeysByObjectID(db *gorm.DB, table string, objectID string) ([]*model.AuthNKeyView, error) { + keys := make([]*model.AuthNKeyView, 0) + queries := []*key_model.AuthNKeySearchQuery{ + { + Key: key_model.AuthNKeyObjectID, + Value: objectID, + Method: global_model.SearchMethodEquals, + }, + } + query := repository.PrepareSearchQuery(table, model.AuthNKeySearchRequest{Queries: queries}) + _, err := query(db, &keys) + if err != nil { + return nil, err + } + return keys, nil +} + +func AuthNKeyByID(db *gorm.DB, table string, keyID string) (*model.AuthNKeyView, error) { + key := new(model.AuthNKeyView) + query := repository.PrepareGetByQuery(table, + model.AuthNKeySearchQuery{Key: key_model.AuthNKeyKeyID, Method: global_model.SearchMethodEquals, Value: keyID}, + ) + err := query(db, key) + if caos_errs.IsNotFound(err) { + return nil, caos_errs.ThrowNotFound(nil, "VIEW-BjN6x", "Errors.User.KeyNotFound") + } + return key, err +} + +func PutAuthNKey(db *gorm.DB, table string, role *model.AuthNKeyView) error { + save := repository.PrepareSave(table) + return save(db, role) +} + +func DeleteAuthNKey(db *gorm.DB, table, keyID string) error { + delete := repository.PrepareDeleteByKey(table, model.AuthNKeySearchKey(key_model.AuthNKeyKeyID), keyID) + return delete(db) +} + +func DeleteAuthNKeysByObjectID(db *gorm.DB, table, objectID string) error { + delete := repository.PrepareDeleteByKey(table, model.AuthNKeySearchKey(key_model.AuthNKeyObjectID), objectID) + return delete(db) +} diff --git a/internal/key/repository/view/model/authn_key.go b/internal/key/repository/view/model/authn_key.go new file mode 100644 index 0000000000..200c5b36be --- /dev/null +++ b/internal/key/repository/view/model/authn_key.go @@ -0,0 +1,171 @@ +package model + +import ( + "encoding/json" + "time" + + "github.com/caos/logging" + + caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/key/model" + proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" + proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model" + user_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" +) + +const ( + AuthNKeyKeyID = "key_id" + AuthNKeyObjectID = "object_id" + AuthNKeyObjectType = "object_type" +) + +type AuthNKeyView struct { + ID string `json:"keyId" gorm:"column:key_id;primary_key"` + ObjectID string `json:"-" gorm:"column:object_id;primary_key"` + ObjectType int32 `json:"-" gorm:"column:object_type;primary_key"` + AuthIdentifier string `json:"-" gorm:"column:auth_identifier;primary_key"` + Type int32 `json:"type" gorm:"column:key_type"` + ExpirationDate time.Time `json:"expirationDate" gorm:"column:expiration_date"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + PublicKey []byte `json:"publicKey" gorm:"column:public_key"` + State int32 `json:"-" gorm:"column:state"` +} + +func AuthNKeyViewFromModel(key *model.AuthNKeyView) *AuthNKeyView { + return &AuthNKeyView{ + ID: key.ID, + ObjectID: key.ObjectID, + ObjectType: int32(key.ObjectType), + Type: int32(key.Type), + ExpirationDate: key.ExpirationDate, + Sequence: key.Sequence, + CreationDate: key.CreationDate, + State: int32(key.State), + } +} + +func AuthNKeyToModel(key *AuthNKeyView) *model.AuthNKeyView { + return &model.AuthNKeyView{ + ID: key.ID, + ObjectID: key.ObjectID, + ObjectType: model.ObjectType(key.ObjectType), + AuthIdentifier: key.AuthIdentifier, + Type: model.AuthNKeyType(key.Type), + ExpirationDate: key.ExpirationDate, + Sequence: key.Sequence, + CreationDate: key.CreationDate, + PublicKey: key.PublicKey, + State: model.AuthNKeyState(key.State), + } +} + +func AuthNKeysToModel(keys []*AuthNKeyView) []*model.AuthNKeyView { + result := make([]*model.AuthNKeyView, len(keys)) + for i, key := range keys { + result[i] = AuthNKeyToModel(key) + } + return result +} + +func (k *AuthNKeyView) AppendEventIfMyClientKey(event *models.Event) (err error) { + switch event.Type { + case proj_model.ApplicationDeactivated, + proj_model.ApplicationReactivated, + proj_model.ApplicationRemoved: + a := new(proj_view_model.ApplicationView) + if err := a.AppendEvent(event); err != nil { + return err + } + if a.ID == k.ObjectID { + return k.AppendEvent(event) + } + case proj_model.ProjectDeactivated, + proj_model.ProjectReactivated, + proj_model.ProjectRemoved: + return k.AppendEvent(event) + case user_model.UserLocked, + user_model.UserDeactivated, + user_model.UserUnlocked, + user_model.UserReactivated, + user_model.UserRemoved: + return k.AppendEvent(event) + case proj_model.ClientKeyRemoved, + user_model.MachineKeyRemoved: + view := new(AuthNKeyView) + if view.ID == k.ID { + return k.AppendEvent(event) + } + default: + return nil + } + return nil +} + +func (k *AuthNKeyView) AppendEvent(event *models.Event) (err error) { + k.Sequence = event.Sequence + switch event.Type { + case user_model.MachineKeyAdded: + k.setRootData(event) + k.CreationDate = event.CreationDate + err = k.SetUserData(event) + case proj_model.ClientKeyAdded: + k.setRootData(event) + k.CreationDate = event.CreationDate + err = k.SetClientData(event) + case proj_model.ClientKeyRemoved, + proj_model.ApplicationRemoved, + proj_model.ProjectRemoved, + user_model.MachineKeyRemoved, + user_model.UserRemoved: + k.State = int32(model.AuthNKeyStateRemoved) + case proj_model.ProjectDeactivated, + proj_model.ApplicationDeactivated, + user_model.UserDeactivated, + user_model.UserLocked: + k.State = int32(model.AuthNKeyStateInactive) + case proj_model.ProjectReactivated, + proj_model.ApplicationReactivated, + user_model.UserReactivated, + user_model.UserUnlocked: + if k.State != int32(model.AuthNKeyStateRemoved) { + k.State = int32(model.AuthNKeyStateActive) + } + } + return err +} + +func (k *AuthNKeyView) setRootData(event *models.Event) { + switch event.AggregateType { + case user_model.UserAggregate: + k.ObjectType = int32(model.AuthNKeyObjectTypeUser) + k.ObjectID = event.AggregateID + k.AuthIdentifier = event.AggregateID + case proj_model.ProjectAggregate: + k.ObjectType = int32(model.AuthNKeyObjectTypeApplication) + } +} + +func (k *AuthNKeyView) SetUserData(event *models.Event) error { + if err := json.Unmarshal(event.Data, k); err != nil { + logging.Log("EVEN-Sj90d").WithError(err).Error("could not unmarshal event data") + return caos_errs.ThrowInternal(err, "MODEL-lub6s", "Could not unmarshal data") + } + return nil +} + +func (k *AuthNKeyView) SetClientData(event *models.Event) error { + key := new(proj_model.ClientKey) + if err := json.Unmarshal(event.Data, key); err != nil { + logging.Log("EVEN-Dgsgg").WithError(err).Error("could not unmarshal event data") + return caos_errs.ThrowInternal(err, "MODEL-ADbfz", "Could not unmarshal data") + } + k.ObjectID = key.ApplicationID + k.AuthIdentifier = key.ClientID + k.ID = key.KeyID + k.ExpirationDate = key.ExpirationDate + k.PublicKey = key.PublicKey + k.Type = key.Type + return nil +} diff --git a/internal/key/repository/view/model/authn_key_query.go b/internal/key/repository/view/model/authn_key_query.go new file mode 100644 index 0000000000..e26c58977e --- /dev/null +++ b/internal/key/repository/view/model/authn_key_query.go @@ -0,0 +1,63 @@ +package model + +import ( + key_model "github.com/caos/zitadel/internal/key/model" + global_model "github.com/caos/zitadel/internal/model" + "github.com/caos/zitadel/internal/view/repository" +) + +type AuthNKeySearchRequest key_model.AuthNKeySearchRequest +type AuthNKeySearchQuery key_model.AuthNKeySearchQuery +type AuthNKeySearchKey key_model.AuthNKeySearchKey + +func (req AuthNKeySearchRequest) GetLimit() uint64 { + return req.Limit +} + +func (req AuthNKeySearchRequest) GetOffset() uint64 { + return req.Offset +} + +func (req AuthNKeySearchRequest) GetSortingColumn() repository.ColumnKey { + if req.SortingColumn == key_model.AuthNKeyKeyUnspecified { + return nil + } + return AuthNKeySearchKey(req.SortingColumn) +} + +func (req AuthNKeySearchRequest) GetAsc() bool { + return req.Asc +} + +func (req AuthNKeySearchRequest) GetQueries() []repository.SearchQuery { + result := make([]repository.SearchQuery, len(req.Queries)) + for i, q := range req.Queries { + result[i] = AuthNKeySearchQuery{Key: q.Key, Value: q.Value, Method: q.Method} + } + return result +} + +func (req AuthNKeySearchQuery) GetKey() repository.ColumnKey { + return AuthNKeySearchKey(req.Key) +} + +func (req AuthNKeySearchQuery) GetMethod() global_model.SearchMethod { + return req.Method +} + +func (req AuthNKeySearchQuery) GetValue() interface{} { + return req.Value +} + +func (key AuthNKeySearchKey) ToColumnName() string { + switch key_model.AuthNKeySearchKey(key) { + case key_model.AuthNKeyKeyID: + return AuthNKeyKeyID + case key_model.AuthNKeyObjectID: + return AuthNKeyObjectID + case key_model.AuthNKeyObjectType: + return AuthNKeyObjectType + default: + return "" + } +} diff --git a/internal/management/repository/eventsourcing/eventstore/permissions.go b/internal/management/repository/eventsourcing/eventstore/permissions.go index 14f3981e46..653792a534 100644 --- a/internal/management/repository/eventsourcing/eventstore/permissions.go +++ b/internal/management/repository/eventsourcing/eventstore/permissions.go @@ -5,5 +5,5 @@ const ( orgMemberReadPerm = "org.member.read" iamMemberReadPerm = "iam.member.read" projectMemberReadPerm = "project.member.read" - projectGrantMemberReadPerm = "project.member.read" + projectGrantMemberReadPerm = "project.grant.member.read" ) diff --git a/internal/management/repository/eventsourcing/eventstore/project.go b/internal/management/repository/eventsourcing/eventstore/project.go index 3c7eb5773e..0324efdbb5 100644 --- a/internal/management/repository/eventsourcing/eventstore/project.go +++ b/internal/management/repository/eventsourcing/eventstore/project.go @@ -10,6 +10,8 @@ import ( caos_errs "github.com/caos/zitadel/internal/errors" es_int "github.com/caos/zitadel/internal/eventstore" iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing" + key_model "github.com/caos/zitadel/internal/key/model" + key_view_model "github.com/caos/zitadel/internal/key/repository/view/model" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" global_model "github.com/caos/zitadel/internal/model" proj_model "github.com/caos/zitadel/internal/project/model" @@ -227,6 +229,10 @@ func (repo *ProjectRepo) ApplicationByID(ctx context.Context, projectID, appID s return model.ApplicationViewToModel(app), nil } +func (repo *ProjectRepo) AddApplication(ctx context.Context, app *proj_model.Application) (*proj_model.Application, error) { + return repo.ProjectEvents.AddApplication(ctx, app) +} + func (repo *ProjectRepo) SearchApplications(ctx context.Context, request *proj_model.ApplicationSearchRequest) (*proj_model.ApplicationSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) sequence, sequenceErr := repo.View.GetLatestApplicationSequence() @@ -268,10 +274,80 @@ func (repo *ProjectRepo) ApplicationChanges(ctx context.Context, id string, appI return changes, nil } +func (repo *ProjectRepo) SearchClientKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) { + request.EnsureLimit(repo.SearchLimit) + sequence, sequenceErr := repo.View.GetLatestAuthNKeySequence() + logging.Log("EVENT-ADwgw").OnError(sequenceErr).Warn("could not read latest authn key sequence") + keys, count, err := repo.View.SearchAuthNKeys(request) + if err != nil { + return nil, err + } + result := &key_model.AuthNKeySearchResponse{ + Offset: request.Offset, + Limit: request.Limit, + TotalResult: count, + Result: key_view_model.AuthNKeysToModel(keys), + } + if sequenceErr == nil { + result.Sequence = sequence.CurrentSequence + result.Timestamp = sequence.LastSuccessfulSpoolerRun + } + return result, nil +} + +func (repo *ProjectRepo) GetClientKey(ctx context.Context, projectID, applicationID, keyID string) (*key_model.AuthNKeyView, error) { + key, viewErr := repo.View.AuthNKeyByIDs(applicationID, keyID) + if viewErr != nil { + return nil, viewErr + } + + events, esErr := repo.ProjectEvents.ProjectEventsByID(ctx, projectID, key.Sequence) + if caos_errs.IsNotFound(viewErr) && len(events) == 0 { + return nil, caos_errs.ThrowNotFound(nil, "EVENT-SFf2g", "Errors.User.KeyNotFound") + } + + if esErr != nil { + logging.Log("EVENT-ADbf2").WithError(viewErr).Debug("error retrieving new events") + return key_view_model.AuthNKeyToModel(key), nil + } + + viewKey := *key + for _, event := range events { + err := key.AppendEventIfMyClientKey(event) + if err != nil { + return key_view_model.AuthNKeyToModel(&viewKey), nil + } + if key.State != int32(proj_model.AppStateActive) { + return nil, caos_errs.ThrowNotFound(nil, "EVENT-Adfg3", "Errors.User.KeyNotFound") + } + } + return key_view_model.AuthNKeyToModel(key), nil +} + +func (repo *ProjectRepo) AddClientKey(ctx context.Context, key *proj_model.ClientKey) (*proj_model.ClientKey, error) { + return repo.ProjectEvents.AddClientKey(ctx, key) +} + +func (repo *ProjectRepo) RemoveClientKey(ctx context.Context, projectID, applicationID, keyID string) error { + return repo.ProjectEvents.RemoveApplicationKey(ctx, projectID, applicationID, keyID) +} + +func (repo *ProjectRepo) ChangeOIDCConfig(ctx context.Context, config *proj_model.OIDCConfig) (*proj_model.OIDCConfig, error) { + return repo.ProjectEvents.ChangeOIDCConfig(ctx, config) +} + +func (repo *ProjectRepo) ChangeAPIConfig(ctx context.Context, config *proj_model.APIConfig) (*proj_model.APIConfig, error) { + return repo.ProjectEvents.ChangeAPIConfig(ctx, config) +} + func (repo *ProjectRepo) ChangeOIDConfigSecret(ctx context.Context, projectID, appID string) (*proj_model.OIDCConfig, error) { return repo.ProjectEvents.ChangeOIDCConfigSecret(ctx, projectID, appID) } +func (repo *ProjectRepo) ChangeAPIConfigSecret(ctx context.Context, projectID, appID string) (*proj_model.APIConfig, error) { + return repo.ProjectEvents.ChangeAPIConfigSecret(ctx, projectID, appID) +} + func (repo *ProjectRepo) ProjectGrantByID(ctx context.Context, grantID string) (*proj_model.ProjectGrantView, error) { grant, err := repo.View.ProjectGrantByID(grantID) if err != nil { diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index 0cf81890bf..7588e23f89 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -4,11 +4,14 @@ import ( "context" "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" + key_model "github.com/caos/zitadel/internal/key/model" + key_view_model "github.com/caos/zitadel/internal/key/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" @@ -184,27 +187,27 @@ func (repo *UserRepo) ExternalIDPsByIDPConfigIDAndResourceOwner(ctx context.Cont return model.ExternalIDPViewsToModel(externalIDPs), nil } -func (repo *UserRepo) GetMachineKey(ctx context.Context, userID, keyID string) (*usr_model.MachineKeyView, error) { - key, err := repo.View.MachineKeyByIDs(userID, keyID) +func (repo *UserRepo) GetMachineKey(ctx context.Context, userID, keyID string) (*key_model.AuthNKeyView, error) { + key, err := repo.View.AuthNKeyByIDs(userID, keyID) if err != nil { return nil, err } - return model.MachineKeyToModel(key), nil + return key_view_model.AuthNKeyToModel(key), nil } -func (repo *UserRepo) SearchMachineKeys(ctx context.Context, request *usr_model.MachineKeySearchRequest) (*usr_model.MachineKeySearchResponse, error) { +func (repo *UserRepo) SearchMachineKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) { request.EnsureLimit(repo.SearchLimit) - 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) + sequence, seqErr := repo.View.GetLatestAuthNKeySequence() + logging.Log("EVENT-Sk8fs").OnError(seqErr).Warn("could not read latest authn key sequence") + keys, count, err := repo.View.SearchAuthNKeys(request) if err != nil { return nil, err } - result := &usr_model.MachineKeySearchResponse{ + result := &key_model.AuthNKeySearchResponse{ Offset: request.Offset, Limit: request.Limit, TotalResult: count, - Result: model.MachineKeysToModel(keys), + Result: key_view_model.AuthNKeysToModel(keys), } if seqErr == nil { result.Sequence = sequence.CurrentSequence diff --git a/internal/management/repository/eventsourcing/handler/application.go b/internal/management/repository/eventsourcing/handler/application.go index c18a055728..71e4c2af07 100644 --- a/internal/management/repository/eventsourcing/handler/application.go +++ b/internal/management/repository/eventsourcing/handler/application.go @@ -87,6 +87,8 @@ func (a *Application) Reduce(event *models.Event) (err error) { case es_model.ApplicationChanged, es_model.OIDCConfigAdded, es_model.OIDCConfigChanged, + es_model.APIConfigAdded, + es_model.APIConfigChanged, es_model.ApplicationDeactivated, es_model.ApplicationReactivated: err = app.SetData(event) diff --git a/internal/management/repository/eventsourcing/handler/authn_keys.go b/internal/management/repository/eventsourcing/handler/authn_keys.go new file mode 100644 index 0000000000..0003010033 --- /dev/null +++ b/internal/management/repository/eventsourcing/handler/authn_keys.go @@ -0,0 +1,120 @@ +package handler + +import ( + "time" + + "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" + key_model "github.com/caos/zitadel/internal/key/repository/view/model" + proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" + user_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" +) + +const ( + authnKeysTable = "management.authn_keys" +) + +type AuthNKeys struct { + handler + subscription *eventstore.Subscription +} + +func newAuthNKeys(handler handler) *AuthNKeys { + h := &AuthNKeys{ + handler: handler, + } + + h.subscribe() + + return h +} + +func (k *AuthNKeys) subscribe() { + k.subscription = k.es.Subscribe(k.AggregateTypes()...) + go func() { + for event := range k.subscription.Events { + query.ReduceEvent(k, event) + } + }() +} + +func (k *AuthNKeys) ViewModel() string { + return authnKeysTable +} + +func (_ *AuthNKeys) AggregateTypes() []es_models.AggregateType { + return []es_models.AggregateType{user_model.UserAggregate, proj_model.ProjectAggregate} +} + +func (k *AuthNKeys) CurrentSequence() (uint64, error) { + sequence, err := k.view.GetLatestAuthNKeySequence() + if err != nil { + return 0, err + } + return sequence.CurrentSequence, nil +} + +func (k *AuthNKeys) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := k.view.GetLatestAuthNKeySequence() + if err != nil { + return nil, err + } + return es_models.NewSearchQuery(). + AggregateTypeFilter(k.AggregateTypes()...). + LatestSequenceFilter(sequence.CurrentSequence), nil +} + +func (k *AuthNKeys) Reduce(event *es_models.Event) (err error) { + switch event.AggregateType { + case user_model.UserAggregate, + proj_model.ProjectAggregate: + err = k.processAuthNKeys(event) + } + return err +} + +func (k *AuthNKeys) processAuthNKeys(event *es_models.Event) (err error) { + key := new(key_model.AuthNKeyView) + switch event.Type { + case user_model.MachineKeyAdded, + proj_model.ClientKeyAdded: + err = key.AppendEvent(event) + if key.ExpirationDate.Before(time.Now()) { + return k.view.ProcessedAuthNKeySequence(event) + } + case user_model.MachineKeyRemoved: + err = key.SetUserData(event) + if err != nil { + return err + } + return k.view.DeleteAuthNKey(key.ID, event) + case proj_model.ClientKeyRemoved: + err = key.SetClientData(event) + if err != nil { + return err + } + return k.view.DeleteAuthNKey(key.ID, event) + case user_model.UserRemoved, + proj_model.ApplicationRemoved: + return k.view.DeleteAuthNKeysByObjectID(event.AggregateID, event) + default: + return k.view.ProcessedAuthNKeySequence(event) + } + if err != nil { + return err + } + return k.view.PutAuthNKey(key, event) +} + +func (d *AuthNKeys) 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.GetLatestAuthNKeyFailedEvent, d.view.ProcessedAuthNKeyFailedEvent, d.view.ProcessedAuthNKeySequence, d.errorCountUntilSkip) +} + +func (d *AuthNKeys) OnSuccess() error { + return spooler.HandleSuccess(d.view.UpdateAuthNKeySpoolerRunTimestamp) +} diff --git a/internal/management/repository/eventsourcing/handler/handler.go b/internal/management/repository/eventsourcing/handler/handler.go index adf3ec26fd..10aa729250 100644 --- a/internal/management/repository/eventsourcing/handler/handler.go +++ b/internal/management/repository/eventsourcing/handler/handler.go @@ -75,7 +75,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}, repos.OrgEvents, repos.ProjectEvents), - newMachineKeys( + newAuthNKeys( handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount, es}), newIDPConfig( handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), diff --git a/internal/management/repository/eventsourcing/handler/machine_keys.go b/internal/management/repository/eventsourcing/handler/machine_keys.go deleted file mode 100644 index cabe5f0cdf..0000000000 --- a/internal/management/repository/eventsourcing/handler/machine_keys.go +++ /dev/null @@ -1,110 +0,0 @@ -package handler - -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" -) - -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() (uint64, error) { - sequence, err := k.view.GetLatestMachineKeySequence() - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (d *MachineKeys) EventQuery() (*models.SearchQuery, error) { - sequence, err := d.view.GetLatestMachineKeySequence() - if err != nil { - return nil, err - } - return es_models.NewSearchQuery(). - AggregateTypeFilter(d.AggregateTypes()...). - LatestSequenceFilter(sequence.CurrentSequence), nil -} - -func (d *MachineKeys) Reduce(event *models.Event) (err error) { - switch event.AggregateType { - case model.UserAggregate: - err = d.processMachineKeys(event) - } - return err -} - -func (d *MachineKeys) processMachineKeys(event *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) - } - case model.MachineKeyRemoved: - err = key.SetData(event) - if err != nil { - return err - } - return d.view.DeleteMachineKey(key.ID, event) - case model.UserRemoved: - return d.view.DeleteMachineKeysByUserID(event.AggregateID, event) - default: - return d.view.ProcessedMachineKeySequence(event) - } - if err != nil { - return err - } - return d.view.PutMachineKey(key, event) -} - -func (d *MachineKeys) OnError(event *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) -} - -func (d *MachineKeys) OnSuccess() error { - return spooler.HandleSuccess(d.view.UpdateMachineKeySpoolerRunTimestamp) -} diff --git a/internal/management/repository/eventsourcing/view/authn_keys.go b/internal/management/repository/eventsourcing/view/authn_keys.go new file mode 100644 index 0000000000..cc982dc4c7 --- /dev/null +++ b/internal/management/repository/eventsourcing/view/authn_keys.go @@ -0,0 +1,74 @@ +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" +) + +const ( + authNKeyTable = "management.authn_keys" +) + +func (v *View) AuthNKeyByIDs(objectID, keyID string) (*model.AuthNKeyView, error) { + return view.AuthNKeyByIDs(v.Db, authNKeyTable, objectID, keyID) +} + +func (v *View) AuthNKeysByObjectID(objectID string) ([]*model.AuthNKeyView, error) { + return view.AuthNKeysByObjectID(v.Db, authNKeyTable, objectID) +} + +func (v *View) AuthNKeyByID(keyID string) (*model.AuthNKeyView, error) { + return view.AuthNKeyByID(v.Db, authNKeyTable, keyID) +} + +func (v *View) SearchAuthNKeys(request *key_model.AuthNKeySearchRequest) ([]*model.AuthNKeyView, uint64, error) { + return view.SearchAuthNKeys(v.Db, authNKeyTable, request) +} + +func (v *View) PutAuthNKey(key *model.AuthNKeyView, event *models.Event) error { + err := view.PutAuthNKey(v.Db, authNKeyTable, key) + if err != nil { + return err + } + return v.ProcessedAuthNKeySequence(event) +} + +func (v *View) DeleteAuthNKey(keyID string, event *models.Event) error { + err := view.DeleteAuthNKey(v.Db, authNKeyTable, keyID) + if err != nil && !errors.IsNotFound(err) { + return err + } + return v.ProcessedAuthNKeySequence(event) +} + +func (v *View) DeleteAuthNKeysByObjectID(objectID string, event *models.Event) error { + err := view.DeleteAuthNKey(v.Db, authNKeyTable, objectID) + if err != nil && !errors.IsNotFound(err) { + return err + } + return v.ProcessedAuthNKeySequence(event) +} + +func (v *View) GetLatestAuthNKeySequence() (*repository.CurrentSequence, error) { + return v.latestSequence(authNKeyTable) +} + +func (v *View) ProcessedAuthNKeySequence(event *models.Event) error { + return v.saveCurrentSequence(authNKeyTable, event) +} + +func (v *View) UpdateAuthNKeySpoolerRunTimestamp() error { + return v.updateSpoolerRunSequence(authNKeyTable) +} + +func (v *View) GetLatestAuthNKeyFailedEvent(sequence uint64) (*repository.FailedEvent, error) { + return v.latestFailedEvent(authNKeyTable, sequence) +} + +func (v *View) ProcessedAuthNKeyFailedEvent(failedEvent *repository.FailedEvent) error { + return v.saveFailedEvent(failedEvent) +} diff --git a/internal/management/repository/eventsourcing/view/machine_keys.go b/internal/management/repository/eventsourcing/view/machine_keys.go deleted file mode 100644 index 075e15fe8e..0000000000 --- a/internal/management/repository/eventsourcing/view/machine_keys.go +++ /dev/null @@ -1,70 +0,0 @@ -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" -) - -const ( - machineKeyTable = "management.machine_keys" -) - -func (v *View) MachineKeyByIDs(userID, keyID string) (*model.MachineKeyView, error) { - return view.MachineKeyByIDs(v.Db, machineKeyTable, userID, keyID) -} - -func (v *View) MachineKeysByUserID(userID string) ([]*model.MachineKeyView, error) { - return view.MachineKeysByUserID(v.Db, machineKeyTable, userID) -} - -func (v *View) SearchMachineKeys(request *usr_model.MachineKeySearchRequest) ([]*model.MachineKeyView, uint64, error) { - return view.SearchMachineKeys(v.Db, machineKeyTable, request) -} - -func (v *View) PutMachineKey(org *model.MachineKeyView, event *models.Event) error { - err := view.PutMachineKey(v.Db, machineKeyTable, org) - if err != nil { - return err - } - return v.ProcessedMachineKeySequence(event) -} - -func (v *View) DeleteMachineKey(keyID string, event *models.Event) error { - err := view.DeleteMachineKey(v.Db, machineKeyTable, keyID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedMachineKeySequence(event) -} - -func (v *View) DeleteMachineKeysByUserID(userID string, event *models.Event) error { - err := view.DeleteMachineKey(v.Db, machineKeyTable, userID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedMachineKeySequence(event) -} - -func (v *View) GetLatestMachineKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(machineKeyTable) -} - -func (v *View) ProcessedMachineKeySequence(event *models.Event) error { - return v.saveCurrentSequence(machineKeyTable, event) -} - -func (v *View) UpdateMachineKeySpoolerRunTimestamp() error { - return v.updateSpoolerRunSequence(machineKeyTable) -} - -func (v *View) GetLatestMachineKeyFailedEvent(sequence uint64) (*repository.FailedEvent, error) { - return v.latestFailedEvent(machineKeyTable, sequence) -} - -func (v *View) ProcessedMachineKeyFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} diff --git a/internal/management/repository/project.go b/internal/management/repository/project.go index 8960006f23..da72adeb7d 100644 --- a/internal/management/repository/project.go +++ b/internal/management/repository/project.go @@ -3,6 +3,7 @@ package repository import ( "context" + key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/project/model" ) @@ -23,9 +24,16 @@ type ProjectRepository interface { ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*model.ProjectChanges, error) ApplicationByID(ctx context.Context, projectID, appID string) (*model.ApplicationView, error) + AddApplication(ctx context.Context, app *model.Application) (*model.Application, error) + ChangeAPIConfig(ctx context.Context, config *model.APIConfig) (*model.APIConfig, error) ChangeOIDConfigSecret(ctx context.Context, projectID, appID string) (*model.OIDCConfig, error) + ChangeAPIConfigSecret(ctx context.Context, projectID, appID string) (*model.APIConfig, error) SearchApplications(ctx context.Context, request *model.ApplicationSearchRequest) (*model.ApplicationSearchResponse, error) ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64, sortAscending bool) (*model.ApplicationChanges, error) + SearchClientKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) + GetClientKey(ctx context.Context, projectID, applicationID, keyID string) (*key_model.AuthNKeyView, error) + AddClientKey(ctx context.Context, key *model.ClientKey) (*model.ClientKey, error) + RemoveClientKey(ctx context.Context, projectID, applicationID, keyID string) error ProjectGrantByID(ctx context.Context, grantID string) (*model.ProjectGrantView, error) SearchProjectGrantMembers(ctx context.Context, request *model.ProjectGrantMemberSearchRequest) (*model.ProjectGrantMemberSearchResponse, error) diff --git a/internal/management/repository/user.go b/internal/management/repository/user.go index 976e0097a0..0eafac5ac7 100644 --- a/internal/management/repository/user.go +++ b/internal/management/repository/user.go @@ -3,6 +3,7 @@ package repository import ( "context" + key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/user/model" ) @@ -26,8 +27,8 @@ type UserRepository interface { ExternalIDPsByIDPConfigID(ctx context.Context, idpConfigID string) ([]*model.ExternalIDPView, error) ExternalIDPsByIDPConfigIDAndResourceOwner(ctx context.Context, idpConfigID, resourceOwner string) ([]*model.ExternalIDPView, error) - SearchMachineKeys(ctx context.Context, request *model.MachineKeySearchRequest) (*model.MachineKeySearchResponse, error) - GetMachineKey(ctx context.Context, userID, keyID string) (*model.MachineKeyView, error) + SearchMachineKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) + GetMachineKey(ctx context.Context, userID, keyID string) (*key_model.AuthNKeyView, error) EmailByID(ctx context.Context, userID string) (*model.Email, error) diff --git a/internal/project/model/api_config.go b/internal/project/model/api_config.go new file mode 100644 index 0000000000..4a3a31202b --- /dev/null +++ b/internal/project/model/api_config.go @@ -0,0 +1,62 @@ +package model + +import ( + "fmt" + "strings" + + "github.com/caos/logging" + + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/id" +) + +type APIConfig struct { + es_models.ObjectRoot + AppID string + ClientID string + ClientSecret *crypto.CryptoValue + ClientSecretString string + AuthMethodType APIAuthMethodType + ClientKeys []*ClientKey +} + +type APIAuthMethodType int32 + +const ( + APIAuthMethodTypeBasic APIAuthMethodType = iota + APIAuthMethodTypePrivateKeyJWT +) + +func (c *APIConfig) IsValid() bool { + return true +} + +//ClientID random_number@projectname (eg. 495894098234@zitadel) +func (c *APIConfig) GenerateNewClientID(idGenerator id.Generator, project *Project) error { + rndID, err := idGenerator.Next() + if err != nil { + return err + } + + c.ClientID = fmt.Sprintf("%v@%v", rndID, strings.ReplaceAll(strings.ToLower(project.Name), " ", "_")) + return nil +} + +func (c *APIConfig) GenerateClientSecretIfNeeded(generator crypto.Generator) (string, error) { + if c.AuthMethodType == APIAuthMethodTypeBasic { + return c.GenerateNewClientSecret(generator) + } + return "", nil +} + +func (c *APIConfig) GenerateNewClientSecret(generator crypto.Generator) (string, error) { + cryptoValue, stringSecret, err := crypto.NewCode(generator) + if err != nil { + logging.Log("MODEL-ADvd2").OnError(err).Error("unable to create client secret") + return "", errors.ThrowInternal(err, "MODEL-dsvr43", "Errors.Project.CouldNotGenerateClientSecret") + } + c.ClientSecret = cryptoValue + return stringSecret, nil +} diff --git a/internal/project/model/application.go b/internal/project/model/application.go index d3dd028810..eba494caab 100644 --- a/internal/project/model/application.go +++ b/internal/project/model/application.go @@ -1,8 +1,9 @@ package model import ( - es_models "github.com/caos/zitadel/internal/eventstore/models" "github.com/golang/protobuf/ptypes/timestamp" + + es_models "github.com/caos/zitadel/internal/eventstore/models" ) type Application struct { @@ -13,6 +14,7 @@ type Application struct { Name string Type AppType OIDCConfig *OIDCConfig + APIConfig *APIConfig } type ApplicationChanges struct { Changes []*ApplicationChange @@ -42,6 +44,7 @@ const ( AppTypeUnspecified AppType = iota AppTypeOIDC AppTypeSAML + AppTypeAPI ) func NewApplication(projectID, appID string) *Application { @@ -58,5 +61,20 @@ func (a *Application) IsValid(includeConfig bool) bool { if a.Type == AppTypeOIDC && !a.OIDCConfig.IsValid() { return false } + if a.Type == AppTypeAPI && !a.APIConfig.IsValid() { + return false + } return true } + +func (a *Application) GetKey(keyID string) (int, *ClientKey) { + if a.OIDCConfig == nil { + return -1, nil + } + for i, k := range a.OIDCConfig.ClientKeys { + if k.KeyID == keyID { + return i, k + } + } + return -1, nil +} diff --git a/internal/project/model/oidc_config.go b/internal/project/model/oidc_config.go index 8c04b9fba5..647c8ac0a8 100644 --- a/internal/project/model/oidc_config.go +++ b/internal/project/model/oidc_config.go @@ -11,6 +11,7 @@ import ( "github.com/caos/zitadel/internal/errors" es_models "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/id" + key_model "github.com/caos/zitadel/internal/key/model" ) const ( @@ -40,6 +41,7 @@ type OIDCConfig struct { IDTokenRoleAssertion bool IDTokenUserinfoAssertion bool ClockSkew time.Duration + ClientKeys []*ClientKey } type OIDCVersion int32 @@ -78,6 +80,7 @@ const ( OIDCAuthMethodTypeBasic OIDCAuthMethodType = iota OIDCAuthMethodTypePost OIDCAuthMethodTypeNone + OIDCAuthMethodTypePrivateKeyJWT ) type Compliance struct { @@ -92,6 +95,27 @@ const ( OIDCTokenTypeJWT ) +type ClientKey struct { + es_models.ObjectRoot + + ApplicationID string + ClientID string + KeyID string + Type key_model.AuthNKeyType + ExpirationDate time.Time + PrivateKey []byte +} + +type Token struct { + es_models.ObjectRoot + + TokenID string + ClientID string + Audience []string + Expiration time.Time + Scopes []string +} + func (c *OIDCConfig) IsValid() bool { grantTypes := c.getRequiredGrantTypes() for _, grantType := range grantTypes { @@ -115,10 +139,10 @@ func (c *OIDCConfig) GenerateNewClientID(idGenerator id.Generator, project *Proj } func (c *OIDCConfig) GenerateClientSecretIfNeeded(generator crypto.Generator) (string, error) { - if c.AuthMethodType == OIDCAuthMethodTypeNone { - return "", nil + if c.AuthMethodType == OIDCAuthMethodTypeBasic || c.AuthMethodType == OIDCAuthMethodTypePost { + return c.GenerateNewClientSecret(generator) } - return c.GenerateNewClientSecret(generator) + return "", nil } func (c *OIDCConfig) GenerateNewClientSecret(generator crypto.Generator) (string, error) { diff --git a/internal/project/repository/eventsourcing/eventstore.go b/internal/project/repository/eventsourcing/eventstore.go index 1b53c4e3e8..f5e211945e 100644 --- a/internal/project/repository/eventsourcing/eventstore.go +++ b/internal/project/repository/eventsourcing/eventstore.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "strings" + "time" "github.com/caos/logging" "github.com/golang/protobuf/ptypes" @@ -18,6 +19,7 @@ import ( es_models "github.com/caos/zitadel/internal/eventstore/models" es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" "github.com/caos/zitadel/internal/id" + key_model "github.com/caos/zitadel/internal/key/model" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" "github.com/caos/zitadel/internal/telemetry/tracing" @@ -30,10 +32,11 @@ const ( type ProjectEventstore struct { es_int.Eventstore - projectCache *ProjectCache - passwordAlg crypto.HashAlgorithm - pwGenerator crypto.Generator - idGenerator id.Generator + projectCache *ProjectCache + passwordAlg crypto.HashAlgorithm + pwGenerator crypto.Generator + idGenerator id.Generator + ClientKeySize int } type ProjectConfig struct { @@ -49,11 +52,12 @@ func StartProject(conf ProjectConfig, systemDefaults sd.SystemDefaults) (*Projec passwordAlg := crypto.NewBCrypt(systemDefaults.SecretGenerators.PasswordSaltCost) pwGenerator := crypto.NewHashGenerator(systemDefaults.SecretGenerators.ClientSecretGenerator, passwordAlg) return &ProjectEventstore{ - Eventstore: conf.Eventstore, - projectCache: projectCache, - passwordAlg: passwordAlg, - pwGenerator: pwGenerator, - idGenerator: id.SonyFlakeGenerator, + Eventstore: conf.Eventstore, + projectCache: projectCache, + passwordAlg: passwordAlg, + pwGenerator: pwGenerator, + idGenerator: id.SonyFlakeGenerator, + ClientKeySize: int(systemDefaults.SecretGenerators.ClientKeySize), }, nil } @@ -513,15 +517,14 @@ func (es *ProjectEventstore) AddApplication(ctx context.Context, app *proj_model if err != nil { return nil, err } - id, err := es.idGenerator.Next() + app.AppID, err = es.idGenerator.Next() if err != nil { return nil, err } - app.AppID = id var stringPw string if app.OIDCConfig != nil { - app.OIDCConfig.AppID = id + app.OIDCConfig.AppID = app.AppID err := app.OIDCConfig.GenerateNewClientID(es.idGenerator, existingProject) if err != nil { return nil, err @@ -531,6 +534,17 @@ func (es *ProjectEventstore) AddApplication(ctx context.Context, app *proj_model return nil, err } } + if app.APIConfig != nil { + app.APIConfig.AppID = app.AppID + err := app.APIConfig.GenerateNewClientID(es.idGenerator, existingProject) + if err != nil { + return nil, err + } + stringPw, err = app.APIConfig.GenerateClientSecretIfNeeded(es.pwGenerator) + if err != nil { + return nil, err + } + } repoProject := model.ProjectFromModel(existingProject) repoApp := model.AppFromModel(app) @@ -542,8 +556,13 @@ func (es *ProjectEventstore) AddApplication(ctx context.Context, app *proj_model es.projectCache.cacheProject(repoProject) if _, a := model.GetApplication(repoProject.Applications, app.AppID); a != nil { converted := model.AppToModel(a) - converted.OIDCConfig.ClientSecretString = stringPw - converted.OIDCConfig.FillCompliance() + if converted.OIDCConfig != nil { + converted.OIDCConfig.ClientSecretString = stringPw + converted.OIDCConfig.FillCompliance() + } + if converted.APIConfig != nil { + converted.APIConfig.ClientSecretString = stringPw + } return converted, nil } return nil, caos_errs.ThrowInternal(nil, "EVENT-GvPct", "Errors.Internal") @@ -748,6 +767,36 @@ func (es *ProjectEventstore) ChangeOIDCConfig(ctx context.Context, config *proj_ return nil, caos_errs.ThrowInternal(nil, "EVENT-dk87s", "Errors.Internal") } +func (es *ProjectEventstore) ChangeAPIConfig(ctx context.Context, config *proj_model.APIConfig) (*proj_model.APIConfig, error) { + if config == nil || !config.IsValid() { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-SDg54", "Errors.Project.APIConfigInvalid") + } + existingProject, err := es.ProjectByID(ctx, config.AggregateID) + if err != nil { + return nil, err + } + var app *proj_model.Application + if _, app = existingProject.GetApp(config.AppID); app == nil { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Rgu63", "Errors.Project.AppNotExisting") + } + if app.Type != proj_model.AppTypeAPI { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-RHj63", "Errors.Project.AppIsNotAPI") + } + repoProject := model.ProjectFromModel(existingProject) + repoConfig := model.APIConfigFromModel(config) + + projectAggregate := APIConfigChangedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoConfig) + err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) + if err != nil { + return nil, err + } + es.projectCache.cacheProject(repoProject) + if _, a := model.GetApplication(repoProject.Applications, app.AppID); a != nil { + return model.APIConfigToModel(a.APIConfig), nil + } + return nil, caos_errs.ThrowInternal(nil, "EVENT-aebn5", "Errors.Internal") +} + func (es *ProjectEventstore) ChangeOIDCConfigSecret(ctx context.Context, projectID, appID string) (*proj_model.OIDCConfig, error) { if appID == "" { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-7ue34", "Errors.Project.App.OIDCConfigInvalid") @@ -763,8 +812,8 @@ func (es *ProjectEventstore) ChangeOIDCConfigSecret(ctx context.Context, project if app.Type != proj_model.AppTypeOIDC { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dile4", "Errors.Project.App.IsNotOIDC") } - if app.OIDCConfig.AuthMethodType == proj_model.OIDCAuthMethodTypeNone { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-GDrg2", "Errors.Project.OIDCAuthMethodNoneSecret") + if app.OIDCConfig.AuthMethodType == proj_model.OIDCAuthMethodTypeNone || app.OIDCConfig.AuthMethodType == proj_model.OIDCAuthMethodTypePrivateKeyJWT { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-GDrg2", "Errors.Project.OIDCAuthMethodNoSecret") } repoProject := model.ProjectFromModel(existingProject) @@ -789,6 +838,47 @@ func (es *ProjectEventstore) ChangeOIDCConfigSecret(ctx context.Context, project return nil, caos_errs.ThrowInternal(nil, "EVENT-dk87s", "Errors.Internal") } +func (es *ProjectEventstore) ChangeAPIConfigSecret(ctx context.Context, projectID, appID string) (*proj_model.APIConfig, error) { + if appID == "" { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-sdfb3", "Errors.Project.APIConfigInvalid") + } + existingProject, err := es.ProjectByID(ctx, projectID) + if err != nil { + return nil, err + } + var app *proj_model.Application + if _, app = existingProject.GetApp(appID); app == nil { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-ADbg3", "Errors.Project.AppNotExisting") + } + if app.Type != proj_model.AppTypeAPI { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Ntwqw", "Errors.Project.AppIsNotAPI") + } + if app.APIConfig.AuthMethodType != proj_model.APIAuthMethodTypeBasic { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-HW4tw", "Errors.Project.APIAuthMethodNoSecret") + } + repoProject := model.ProjectFromModel(existingProject) + + stringPw, err := app.APIConfig.GenerateNewClientSecret(es.pwGenerator) + if err != nil { + return nil, err + } + + projectAggregate := APIConfigSecretChangedAggregate(es.Eventstore.AggregateCreator(), repoProject, appID, app.APIConfig.ClientSecret) + err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) + if err != nil { + return nil, err + } + es.projectCache.cacheProject(repoProject) + + if _, a := model.GetApplication(repoProject.Applications, app.AppID); a != nil { + config := model.APIConfigToModel(a.APIConfig) + config.ClientSecretString = stringPw + return config, nil + } + + return nil, caos_errs.ThrowInternal(nil, "EVENT-HBfju", "Errors.Internal") +} + func (es *ProjectEventstore) VerifyOIDCClientSecret(ctx context.Context, projectID, appID string, secret string) (err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() @@ -831,6 +921,81 @@ func (es *ProjectEventstore) setOIDCClientSecretCheckResult(ctx context.Context, return nil } +func (es *ProjectEventstore) AddClientKey(ctx context.Context, key *proj_model.ClientKey) (*proj_model.ClientKey, error) { + existingProject, err := es.ProjectByID(ctx, key.AggregateID) + if err != nil { + return nil, err + } + var app *proj_model.Application + if _, app = existingProject.GetApp(key.ApplicationID); app == nil { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Dbf32", "Errors.Project.AppNoExisting") + } + if (app.OIDCConfig == nil || app.OIDCConfig != nil && app.OIDCConfig.AuthMethodType != proj_model.OIDCAuthMethodTypePrivateKeyJWT) && + (app.APIConfig == nil || app.APIConfig != nil && app.APIConfig.AuthMethodType != proj_model.APIAuthMethodTypePrivateKeyJWT) { + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Dff54", "Errors.Project.AuthMethodNoPrivateKeyJWT") + } + if app.OIDCConfig != nil { + key.ClientID = app.OIDCConfig.ClientID + } + if app.APIConfig != nil { + key.ClientID = app.APIConfig.ClientID + } + key.KeyID, err = es.idGenerator.Next() + if err != nil { + return nil, err + } + if key.ExpirationDate.IsZero() { + key.ExpirationDate, err = key_model.DefaultExpiration() + if err != nil { + logging.Log("EVENT-Adgf2").WithError(err).Warn("unable to set default date") + return nil, errors.ThrowInternal(err, "EVENT-j68fg", "Errors.Internal") + } + } + if key.ExpirationDate.Before(time.Now()) { + return nil, errors.ThrowInvalidArgument(nil, "EVENT-C6YV5", "Errors.MachineKey.ExpireBeforeNow") + } + + repoProject := model.ProjectFromModel(existingProject) + repoKey := model.ClientKeyFromModel(key) + err = repoKey.GenerateClientKeyPair(es.ClientKeySize) + if err != nil { + return nil, err + } + agg := OIDCApplicationKeyAddedAggregate(es.AggregateCreator(), repoProject, repoKey) + err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, agg) + if err != nil { + return nil, err + } + es.projectCache.cacheProject(repoProject) + + return model.ClientKeyToModel(repoKey), nil +} + +func (es *ProjectEventstore) RemoveApplicationKey(ctx context.Context, projectID, applicationID, keyID string) error { + existingProject, err := es.ProjectByID(ctx, projectID) + if err != nil { + return err + } + var app *proj_model.Application + if _, app = existingProject.GetApp(applicationID); app == nil { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-ADfzz", "Errors.Project.AppNotExisting") + } + if app.Type != proj_model.AppTypeOIDC { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-ADffh", "Errors.Project.AppIsNotOIDC") + } + if _, key := app.GetKey(keyID); key == nil { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-D2Sff", "Errors.Project.AppKeyNotExisting") + } + repoProject := model.ProjectFromModel(existingProject) + agg := OIDCApplicationKeyRemovedAggregate(es.AggregateCreator(), repoProject, keyID) + err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, agg) + if err != nil { + return err + } + es.projectCache.cacheProject(repoProject) + return nil +} + func (es *ProjectEventstore) ProjectGrantByIDs(ctx context.Context, projectID, grantID string) (*proj_model.ProjectGrant, error) { if grantID == "" { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-e8die", "Errors.Project.IDMissing") diff --git a/internal/project/repository/eventsourcing/model/api_config.go b/internal/project/repository/eventsourcing/model/api_config.go new file mode 100644 index 0000000000..493b817c14 --- /dev/null +++ b/internal/project/repository/eventsourcing/model/api_config.go @@ -0,0 +1,87 @@ +package model + +import ( + "encoding/json" + + "github.com/caos/logging" + + "github.com/caos/zitadel/internal/crypto" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/project/model" +) + +type APIConfig struct { + es_models.ObjectRoot + AppID string `json:"appId"` + ClientID string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + AuthMethodType int32 `json:"authMethodType,omitempty"` + ClientKeys []*ClientKey `json:"-"` +} + +func (c *APIConfig) Changes(changed *APIConfig) map[string]interface{} { + changes := make(map[string]interface{}, 1) + changes["appId"] = c.AppID + if c.AuthMethodType != changed.AuthMethodType { + changes["authMethodType"] = changed.AuthMethodType + } + return changes +} + +func APIConfigFromModel(config *model.APIConfig) *APIConfig { + return &APIConfig{ + ObjectRoot: config.ObjectRoot, + AppID: config.AppID, + ClientID: config.ClientID, + ClientSecret: config.ClientSecret, + AuthMethodType: int32(config.AuthMethodType), + } +} + +func APIConfigToModel(config *APIConfig) *model.APIConfig { + oidcConfig := &model.APIConfig{ + ObjectRoot: config.ObjectRoot, + AppID: config.AppID, + ClientID: config.ClientID, + ClientSecret: config.ClientSecret, + AuthMethodType: model.APIAuthMethodType(config.AuthMethodType), + ClientKeys: ClientKeysToModel(config.ClientKeys), + } + return oidcConfig +} + +func (p *Project) appendAddAPIConfigEvent(event *es_models.Event) error { + config := new(APIConfig) + err := config.setData(event) + if err != nil { + return err + } + config.ObjectRoot.CreationDate = event.CreationDate + if i, a := GetApplication(p.Applications, config.AppID); a != nil { + p.Applications[i].Type = int32(model.AppTypeAPI) + p.Applications[i].APIConfig = config + } + return nil +} + +func (p *Project) appendChangeAPIConfigEvent(event *es_models.Event) error { + config := new(APIConfig) + err := config.setData(event) + if err != nil { + return err + } + + if i, a := GetApplication(p.Applications, config.AppID); a != nil { + return p.Applications[i].APIConfig.setData(event) + } + return nil +} + +func (o *APIConfig) setData(event *es_models.Event) error { + o.ObjectRoot.AppendEvent(event) + if err := json.Unmarshal(event.Data, o); err != nil { + logging.Log("EVEN-d8e3s").WithError(err).Error("could not unmarshal event data") + return err + } + return nil +} diff --git a/internal/project/repository/eventsourcing/model/application.go b/internal/project/repository/eventsourcing/model/application.go index 72da706244..a08228c5da 100644 --- a/internal/project/repository/eventsourcing/model/application.go +++ b/internal/project/repository/eventsourcing/model/application.go @@ -2,7 +2,9 @@ package model import ( "encoding/json" + "github.com/caos/logging" + es_models "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/project/model" ) @@ -14,6 +16,7 @@ type Application struct { Name string `json:"name,omitempty"` Type int32 `json:"appType,omitempty"` OIDCConfig *OIDCConfig `json:"-"` + APIConfig *APIConfig `json:"-"` } type ApplicationID struct { @@ -66,6 +69,9 @@ func AppFromModel(app *model.Application) *Application { if app.OIDCConfig != nil { converted.OIDCConfig = OIDCConfigFromModel(app.OIDCConfig) } + if app.APIConfig != nil { + converted.APIConfig = APIConfigFromModel(app.APIConfig) + } return converted } @@ -80,6 +86,9 @@ func AppToModel(app *Application) *model.Application { if app.OIDCConfig != nil { converted.OIDCConfig = OIDCConfigToModel(app.OIDCConfig) } + if app.APIConfig != nil { + converted.APIConfig = APIConfigToModel(app.APIConfig) + } return converted } @@ -101,7 +110,7 @@ func (p *Project) appendChangeAppEvent(event *es_models.Event) error { return err } if i, a := GetApplication(p.Applications, app.AppID); a != nil { - p.Applications[i].setData(event) + return p.Applications[i].setData(event) } return nil } diff --git a/internal/project/repository/eventsourcing/model/oidc_config.go b/internal/project/repository/eventsourcing/model/oidc_config.go index c489742936..635d78d2fd 100644 --- a/internal/project/repository/eventsourcing/model/oidc_config.go +++ b/internal/project/repository/eventsourcing/model/oidc_config.go @@ -8,7 +8,9 @@ import ( "github.com/caos/logging" "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" es_models "github.com/caos/zitadel/internal/eventstore/models" + key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/project/model" ) @@ -30,6 +32,7 @@ type OIDCConfig struct { IDTokenRoleAssertion bool `json:"idTokenRoleAssertion,omitempty"` IDTokenUserinfoAssertion bool `json:"idTokenUserinfoAssertion,omitempty"` ClockSkew time.Duration `json:"clockSkew,omitempty"` + ClientKeys []*ClientKey `json:"-"` } func (c *OIDCConfig) Changes(changed *OIDCConfig) map[string]interface{} { @@ -134,6 +137,7 @@ func OIDCConfigToModel(config *OIDCConfig) *model.OIDCConfig { IDTokenRoleAssertion: config.IDTokenRoleAssertion, IDTokenUserinfoAssertion: config.IDTokenUserinfoAssertion, ClockSkew: config.ClockSkew, + ClientKeys: ClientKeysToModel(config.ClientKeys), } oidcConfig.FillCompliance() return oidcConfig @@ -161,7 +165,50 @@ func (p *Project) appendChangeOIDCConfigEvent(event *es_models.Event) error { } if i, a := GetApplication(p.Applications, config.AppID); a != nil { - p.Applications[i].OIDCConfig.setData(event) + return p.Applications[i].OIDCConfig.setData(event) + } + return nil +} + +func (p *Project) appendAddClientKeyEvent(event *es_models.Event) error { + key := new(ClientKey) + err := key.SetData(event) + if err != nil { + return err + } + + if i, a := GetApplication(p.Applications, key.ApplicationID); a != nil { + if a.OIDCConfig != nil { + p.Applications[i].OIDCConfig.ClientKeys = append(p.Applications[i].OIDCConfig.ClientKeys, key) + } + if a.APIConfig != nil { + p.Applications[i].APIConfig.ClientKeys = append(p.Applications[i].APIConfig.ClientKeys, key) + } + } + return nil +} + +func (p *Project) appendRemoveClientKeyEvent(event *es_models.Event) error { + key := new(ClientKey) + err := key.SetData(event) + if err != nil { + return err + } + if i, a := GetApplication(p.Applications, key.ApplicationID); a != nil { + if a.OIDCConfig != nil { + if j, k := GetClientKey(p.Applications[i].OIDCConfig.ClientKeys, key.KeyID); k != nil { + p.Applications[i].OIDCConfig.ClientKeys[j] = p.Applications[i].OIDCConfig.ClientKeys[len(p.Applications[i].OIDCConfig.ClientKeys)-1] + p.Applications[i].OIDCConfig.ClientKeys[len(p.Applications[i].OIDCConfig.ClientKeys)-1] = nil + p.Applications[i].OIDCConfig.ClientKeys = p.Applications[i].OIDCConfig.ClientKeys[:len(p.Applications[i].OIDCConfig.ClientKeys)-1] + } + } + if a.APIConfig != nil { + if j, k := GetClientKey(p.Applications[i].APIConfig.ClientKeys, key.KeyID); k != nil { + p.Applications[i].APIConfig.ClientKeys[j] = p.Applications[i].APIConfig.ClientKeys[len(p.Applications[i].APIConfig.ClientKeys)-1] + p.Applications[i].APIConfig.ClientKeys[len(p.Applications[i].APIConfig.ClientKeys)-1] = nil + p.Applications[i].APIConfig.ClientKeys = p.Applications[i].APIConfig.ClientKeys[:len(p.Applications[i].APIConfig.ClientKeys)-1] + } + } } return nil } @@ -174,3 +221,100 @@ func (o *OIDCConfig) setData(event *es_models.Event) error { } return nil } + +func GetClientKey(keys []*ClientKey, id string) (int, *ClientKey) { + for i, k := range keys { + if k.KeyID == id { + return i, k + } + } + return -1, nil +} + +type ClientKey struct { + es_models.ObjectRoot `json:"-"` + ApplicationID string `json:"applicationId,omitempty"` + ClientID string `json:"clientId,omitempty"` + KeyID string `json:"keyId,omitempty"` + Type int32 `json:"type,omitempty"` + ExpirationDate time.Time `json:"expirationDate,omitempty"` + PublicKey []byte `json:"publicKey,omitempty"` + privateKey []byte +} + +func (key *ClientKey) SetData(event *es_models.Event) error { + key.ObjectRoot.AppendEvent(event) + if err := json.Unmarshal(event.Data, key); err != nil { + logging.Log("EVEN-SADdg").WithError(err).Error("could not unmarshal event data") + return err + } + return nil +} + +func (key *ClientKey) AppendEvents(events ...*es_models.Event) error { + for _, event := range events { + err := key.AppendEvent(event) + if err != nil { + return err + } + } + return nil +} + +func (key *ClientKey) AppendEvent(event *es_models.Event) (err error) { + key.ObjectRoot.AppendEvent(event) + switch event.Type { + case ClientKeyAdded: + err = json.Unmarshal(event.Data, key) + if err != nil { + return errors.ThrowInternal(err, "MODEL-Fetg3", "Errors.Internal") + } + case ClientKeyRemoved: + key.ExpirationDate = event.CreationDate + } + return err +} + +func ClientKeyFromModel(key *model.ClientKey) *ClientKey { + return &ClientKey{ + ObjectRoot: key.ObjectRoot, + ExpirationDate: key.ExpirationDate, + ApplicationID: key.ApplicationID, + ClientID: key.ClientID, + KeyID: key.KeyID, + Type: int32(key.Type), + } +} + +func ClientKeysToModel(keys []*ClientKey) []*model.ClientKey { + clientKeys := make([]*model.ClientKey, len(keys)) + for i, key := range keys { + clientKeys[i] = ClientKeyToModel(key) + } + return clientKeys +} + +func ClientKeyToModel(key *ClientKey) *model.ClientKey { + return &model.ClientKey{ + ObjectRoot: key.ObjectRoot, + ExpirationDate: key.ExpirationDate, + ApplicationID: key.ApplicationID, + ClientID: key.ClientID, + KeyID: key.KeyID, + PrivateKey: key.privateKey, + Type: key_model.AuthNKeyType(key.Type), + } +} + +func (key *ClientKey) GenerateClientKeyPair(keySize int) error { + privateKey, publicKey, err := crypto.GenerateKeyPair(keySize) + if err != nil { + return err + } + key.PublicKey, err = crypto.PublicKeyToBytes(publicKey) + if err != nil { + return err + } + key.privateKey = crypto.PrivateKeyToBytes(privateKey) + return nil +} diff --git a/internal/project/repository/eventsourcing/model/project.go b/internal/project/repository/eventsourcing/model/project.go index c0f8d8e35a..bdf133c300 100644 --- a/internal/project/repository/eventsourcing/model/project.go +++ b/internal/project/repository/eventsourcing/model/project.go @@ -139,6 +139,14 @@ func (p *Project) AppendEvent(event *es_models.Event) error { return p.appendAddOIDCConfigEvent(event) case OIDCConfigChanged, OIDCConfigSecretChanged: return p.appendChangeOIDCConfigEvent(event) + case APIConfigAdded: + return p.appendAddAPIConfigEvent(event) + case APIConfigChanged, APIConfigSecretChanged: + return p.appendChangeAPIConfigEvent(event) + case ClientKeyAdded: + return p.appendAddClientKeyEvent(event) + case ClientKeyRemoved: + return p.appendRemoveClientKeyEvent(event) case ProjectGrantAdded: return p.appendAddGrantEvent(event) case ProjectGrantChanged, ProjectGrantCascadeChanged: diff --git a/internal/project/repository/eventsourcing/model/types.go b/internal/project/repository/eventsourcing/model/types.go index 2bf2f3845a..581627142d 100644 --- a/internal/project/repository/eventsourcing/model/types.go +++ b/internal/project/repository/eventsourcing/model/types.go @@ -41,4 +41,11 @@ const ( OIDCConfigSecretChanged models.EventType = "project.application.config.oidc.secret.changed" OIDCClientSecretCheckSucceeded models.EventType = "project.application.oidc.secret.check.succeeded" OIDCClientSecretCheckFailed models.EventType = "project.application.oidc.secret.check.failed" + + APIConfigAdded models.EventType = "project.application.config.api.added" + APIConfigChanged models.EventType = "project.application.config.api.changed" + APIConfigSecretChanged models.EventType = "project.application.config.api.secret.changed" + + ClientKeyAdded models.EventType = "project.application.oidc.key.added" + ClientKeyRemoved models.EventType = "project.application.oidc.key.removed" ) diff --git a/internal/project/repository/eventsourcing/project.go b/internal/project/repository/eventsourcing/project.go index 5d41db2e4a..363231612b 100644 --- a/internal/project/repository/eventsourcing/project.go +++ b/internal/project/repository/eventsourcing/project.go @@ -36,6 +36,14 @@ func ProjectAggregate(ctx context.Context, aggCreator *es_models.AggregateCreato return aggCreator.NewAggregate(ctx, project.AggregateID, model.ProjectAggregate, model.ProjectVersion, project.Sequence) } +func ProjectAggregateOverwriteContext(ctx context.Context, aggCreator *es_models.AggregateCreator, project *model.Project, resourceOwnerID string, userID string) (*es_models.Aggregate, error) { + if project == nil { + return nil, errors.ThrowPreconditionFailed(nil, "EVENT-ADv2r", "Errors.Internal") + } + + return aggCreator.NewAggregate(ctx, project.AggregateID, model.ProjectAggregate, model.ProjectVersion, project.Sequence, es_models.OverwriteResourceOwner(resourceOwnerID), es_models.OverwriteEditorUser(userID)) +} + func ProjectCreateAggregate(aggCreator *es_models.AggregateCreator, project *model.Project, member *model.ProjectMember) func(ctx context.Context) (*es_models.Aggregate, error) { return func(ctx context.Context) (*es_models.Aggregate, error) { if project == nil || member == nil { @@ -229,6 +237,9 @@ func ApplicationAddedAggregate(aggCreator *es_models.AggregateCreator, existingP if app.OIDCConfig != nil { agg.AppendEvent(model.OIDCConfigAdded, app.OIDCConfig) } + if app.APIConfig != nil { + agg.AppendEvent(model.APIConfigAdded, app.APIConfig) + } return agg, nil } } @@ -322,6 +333,29 @@ func OIDCConfigChangedAggregate(aggCreator *es_models.AggregateCreator, existing } } +func APIConfigChangedAggregate(aggCreator *es_models.AggregateCreator, existingProject *model.Project, config *model.APIConfig) func(ctx context.Context) (*es_models.Aggregate, error) { + return func(ctx context.Context) (*es_models.Aggregate, error) { + if config == nil { + return nil, errors.ThrowPreconditionFailed(nil, "EVENT-slf32", "Errors.Internal") + } + agg, err := ProjectAggregate(ctx, aggCreator, existingProject) + if err != nil { + return nil, err + } + var changes map[string]interface{} + for _, a := range existingProject.Applications { + if a.AppID == config.AppID { + if a.APIConfig != nil { + changes = a.APIConfig.Changes(config) + } + } + } + agg.AppendEvent(model.APIConfigChanged, changes) + + return agg, nil + } +} + func OIDCConfigSecretChangedAggregate(aggCreator *es_models.AggregateCreator, existingProject *model.Project, appID string, secret *crypto.CryptoValue) func(ctx context.Context) (*es_models.Aggregate, error) { return func(ctx context.Context) (*es_models.Aggregate, error) { agg, err := ProjectAggregate(ctx, aggCreator, existingProject) @@ -338,6 +372,22 @@ func OIDCConfigSecretChangedAggregate(aggCreator *es_models.AggregateCreator, ex } } +func APIConfigSecretChangedAggregate(aggCreator *es_models.AggregateCreator, existingProject *model.Project, appID string, secret *crypto.CryptoValue) func(ctx context.Context) (*es_models.Aggregate, error) { + return func(ctx context.Context) (*es_models.Aggregate, error) { + agg, err := ProjectAggregate(ctx, aggCreator, existingProject) + if err != nil { + return nil, err + } + changes := make(map[string]interface{}, 2) + changes["appId"] = appID + changes["clientSecret"] = secret + + agg.AppendEvent(model.APIConfigSecretChanged, changes) + + return agg, nil + } +} + func OIDCClientSecretCheckSucceededAggregate(aggCreator *es_models.AggregateCreator, existingProject *model.Project, appID string) es_sdk.AggregateFunc { return func(ctx context.Context) (*es_models.Aggregate, error) { agg, err := ProjectAggregate(ctx, aggCreator, existingProject) @@ -368,6 +418,33 @@ func OIDCClientSecretCheckFailedAggregate(aggCreator *es_models.AggregateCreator } } +func OIDCApplicationKeyAddedAggregate(aggCreator *es_models.AggregateCreator, existingProject *model.Project, key *model.ClientKey) es_sdk.AggregateFunc { + return func(ctx context.Context) (*es_models.Aggregate, error) { + agg, err := ProjectAggregate(ctx, aggCreator, existingProject) + if err != nil { + return nil, err + } + agg.AppendEvent(model.ClientKeyAdded, key) + + return agg, nil + } +} + +func OIDCApplicationKeyRemovedAggregate(aggCreator *es_models.AggregateCreator, existingProject *model.Project, keyID string) es_sdk.AggregateFunc { + return func(ctx context.Context) (*es_models.Aggregate, error) { + agg, err := ProjectAggregate(ctx, aggCreator, existingProject) + if err != nil { + return nil, err + } + changes := make(map[string]interface{}, 1) + changes["keyId"] = keyID + + agg.AppendEvent(model.ClientKeyRemoved, changes) + + return agg, nil + } +} + func ProjectGrantAddedAggregate(aggCreator *es_models.AggregateCreator, project *model.Project, grant *model.ProjectGrant) func(ctx context.Context) (*es_models.Aggregate, error) { return func(ctx context.Context) (*es_models.Aggregate, error) { if grant == nil { diff --git a/internal/project/repository/view/model/application.go b/internal/project/repository/view/model/application.go index 9cfa7d2684..fe682d8f02 100644 --- a/internal/project/repository/view/model/application.go +++ b/internal/project/repository/view/model/application.go @@ -116,17 +116,25 @@ func (a *ApplicationView) AppendEventIfMyApp(event *models.Event) (err error) { switch event.Type { case es_model.ApplicationAdded: err = view.SetData(event) + if err != nil { + return err + } case es_model.ApplicationChanged, es_model.OIDCConfigAdded, es_model.OIDCConfigChanged, + es_model.APIConfigAdded, + es_model.APIConfigChanged, es_model.ApplicationDeactivated, es_model.ApplicationReactivated: - err := view.SetData(event) + err = view.SetData(event) if err != nil { return err } case es_model.ApplicationRemoved: - return view.SetData(event) + err = view.SetData(event) + if err != nil { + return err + } case es_model.ProjectChanged: return a.AppendEvent(event) case es_model.ProjectRemoved: @@ -156,14 +164,20 @@ func (a *ApplicationView) AppendEvent(event *models.Event) (err error) { } a.setCompliance() return a.setOriginAllowList() - case es_model.OIDCConfigChanged, - es_model.ApplicationChanged: + case es_model.APIConfigAdded: + a.IsOIDC = false + return a.SetData(event) + case es_model.ApplicationChanged: + return a.SetData(event) + case es_model.OIDCConfigChanged: err = a.SetData(event) if err != nil { return err } a.setCompliance() return a.setOriginAllowList() + case es_model.APIConfigChanged: + return a.SetData(event) case es_model.ProjectChanged: return a.SetData(event) case es_model.ApplicationDeactivated: diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 3e6472808c..306a37c4a5 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -189,10 +189,14 @@ Errors: Invalid: Applikation ist ungültig NotExisting: Applikation exisitert nicht IsNotOIDC: Applikation ist nicht vom Typ OIDC + IsNotAPI: Applikation ist nicht vom Typ API NotActive: Applikation ist nicht aktiv NotInactive: Applikation ist nickt inaktiv OIDCConfigInvalid: OIDC Konfiguration ist ungültig - OIDCAuthMethodNoneSecret: OIDC Auth Method None benötigt kein Secret + APIConfigInvalid: API Konfiguration ist ungültig + OIDCAuthMethodNoSecret: Gewählte OIDC Auth Method benötigt kein Secret + APIAuthMethodNoSecret: Gewählte API Auth Method benötigt kein Secret + AuthMethodNoPrivateKeyJWT: Gewählte Auth Method benötigt keinen Key RequiredFieldsMissing: Benötigte Felder fehlen Grant: AlreadyExists: Projekt Grant existiert bereits diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 8c2a785e77..7a19e2f03d 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -190,8 +190,13 @@ Errors: IsNotOIDC: Application is not type oidc NotActive:: Application is not active NotInactive: Application is not inactive - OIDCConfigInvalid: OIDC configuration is invalid - OIDCAuthMethodNoneSecret: OIDC Auth Method None does not require a secret + OIDCConfigInvalid: OIDC configuration is invalid + APIConfigInvalid: OIDC configuration is invalid + IsNotOIDC: Application is not type oidc + IsNotAPI: Application is not type API + OIDCAuthMethodNoSecret: Chosen OIDC Auth Method does not require a secret + APIAuthMethodNoSecret: Chosen API Auth Method does not require a secret + AuthMethodNoPrivateKeyJWT: Chosen Auth Method does not require a key RequiredFieldsMissing: Some required fields are missing Grant: AlreadyExists: Project grant already exists diff --git a/internal/ui/login/handler/external_login_handler.go b/internal/ui/login/handler/external_login_handler.go index c640319818..789d1d905e 100644 --- a/internal/ui/login/handler/external_login_handler.go +++ b/internal/ui/login/handler/external_login_handler.go @@ -1,8 +1,8 @@ package handler import ( + "github.com/caos/oidc/pkg/client/rp" "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/rp" http_mw "github.com/caos/zitadel/internal/api/http/middleware" "github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/errors" @@ -113,13 +113,13 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque l.handleExternalUserAuthenticated(w, r, authReq, idpConfig, userAgentID, tokens) } -func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) rp.RelayingParty { +func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) rp.RelyingParty { oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.IDPConfigAesCrypto) if err != nil { l.renderError(w, r, authReq, err) return nil } - provider, err := rp.NewRelayingPartyOIDC(idpConfig.OIDCIssuer, idpConfig.OIDCClientID, oidcClientSecret, l.baseURL+callbackEndpoint, idpConfig.OIDCScopes, rp.WithVerifierOpts(rp.WithIssuedAtOffset(3*time.Second))) + provider, err := rp.NewRelyingPartyOIDC(idpConfig.OIDCIssuer, idpConfig.OIDCClientID, oidcClientSecret, l.baseURL+callbackEndpoint, idpConfig.OIDCScopes, rp.WithVerifierOpts(rp.WithIssuedAtOffset(3*time.Second))) if err != nil { l.renderError(w, r, authReq, err) return nil diff --git a/internal/ui/login/handler/external_register_handler.go b/internal/ui/login/handler/external_register_handler.go index b1907df24a..4d5f63accf 100644 --- a/internal/ui/login/handler/external_register_handler.go +++ b/internal/ui/login/handler/external_register_handler.go @@ -1,15 +1,16 @@ package handler import ( - "github.com/caos/zitadel/internal/v2/domain" "net/http" "strings" + "github.com/caos/oidc/pkg/client/rp" "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/rp" + http_mw "github.com/caos/zitadel/internal/api/http/middleware" caos_errors "github.com/caos/zitadel/internal/errors" iam_model "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/v2/domain" ) func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) { diff --git a/internal/ui/login/static/resources/scripts/form_submit.js b/internal/ui/login/static/resources/scripts/form_submit.js index ddf94f0b1d..b822c4c35b 100644 --- a/internal/ui/login/static/resources/scripts/form_submit.js +++ b/internal/ui/login/static/resources/scripts/form_submit.js @@ -6,6 +6,8 @@ function disableSubmit(checks, button) { } addRequiredEventListener(inputs, checks, form, button); disableDoubleSubmit(form, button); + + toggleButton(checks, form, inputs, button); } function addRequiredEventListener(inputs, checks, form, button) { diff --git a/internal/ui/login/static/resources/scripts/loginname_suffix.js b/internal/ui/login/static/resources/scripts/loginname_suffix.js index 9e8ce2c5ea..c4aa938bbf 100644 --- a/internal/ui/login/static/resources/scripts/loginname_suffix.js +++ b/internal/ui/login/static/resources/scripts/loginname_suffix.js @@ -10,6 +10,8 @@ function RenderDefaultLoginnameSuffix() { } else { defaultLoginNameSuffix.innerText = ""; } + + offsetLabel(); } window.addEventListener('DOMContentLoaded', (event) => { @@ -19,3 +21,18 @@ window.addEventListener('DOMContentLoaded', (event) => { document.getElementById("orgname").addEventListener('input', function () { RenderDefaultLoginnameSuffix(); }); + +function offsetLabel() { + const suffix = document.getElementById('default-login-suffix'); + const suffixInput = document.getElementsByClassName('lgn-suffix-input')[0]; + + calculateOffset(); + suffix.addEventListener("DOMCharacterDataModified", calculateOffset); + + function calculateOffset() { + // add suffix width to inner right padding of the input field + if (suffix && suffixInput) { + suffixInput.style.paddingRight = `${(suffix.offsetWidth ?? 0) + 10}px`; + } + } +} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/scss/styles/account_selection/account_selection.scss b/internal/ui/login/static/resources/themes/scss/styles/account_selection/account_selection.scss index c090205d7d..a4d8ac04a3 100644 --- a/internal/ui/login/static/resources/themes/scss/styles/account_selection/account_selection.scss +++ b/internal/ui/login/static/resources/themes/scss/styles/account_selection/account_selection.scss @@ -21,16 +21,21 @@ $lgn-container-margin: 0px auto 50px auto; display: block; margin: .5rem; text-align: start; + overflow: hidden; .lgn-displayname{ font-size: 16px; margin: .5rem 0; margin-bottom: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-loginname{ font-size: 14px; margin: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-session-state { diff --git a/internal/ui/login/static/resources/themes/zitadel/css/bundle.css b/internal/ui/login/static/resources/themes/zitadel/css/bundle.css index 3d864d0328..d21ea24fea 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/bundle.css +++ b/internal/ui/login/static/resources/themes/zitadel/css/bundle.css @@ -448,15 +448,20 @@ i { display: block; margin: 0.5rem; text-align: start; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-displayname { font-size: 16px; margin: 0.5rem 0; margin-bottom: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-loginname { font-size: 14px; margin: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-session-state { margin: 0; @@ -1301,15 +1306,20 @@ i { display: block; margin: 0.5rem; text-align: start; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-displayname { font-size: 16px; margin: 0.5rem 0; margin-bottom: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-loginname { font-size: 14px; margin: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-session-state { margin: 0; diff --git a/internal/ui/login/static/resources/themes/zitadel/css/bundle.css.map b/internal/ui/login/static/resources/themes/zitadel/css/bundle.css.map index b889956c74..b21900164a 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/bundle.css.map +++ b/internal/ui/login/static/resources/themes/zitadel/css/bundle.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/styles/footer/footer.scss","../../scss/styles/header/header.scss","../../scss/styles/button/button.scss","../../scss/styles/button/button_base.scss","../../scss/styles/input/input.scss","../../scss/styles/input/input_base.scss","../../scss/styles/label/label.scss","../../scss/styles/label/label_base.scss","../../scss/styles/radio/radio_base.scss","../../scss/styles/radio/radio.scss","../../scss/styles/a/a.scss","../../scss/styles/identity_provider/identity_provider.scss","../../scss/styles/identity_provider/identity_provider_base.scss","../../scss/styles/error/error.scss","../../scss/styles/qrcode/qrcode.scss","../../scss/styles/container/container.scss","../../scss/styles/account_selection/account_selection.scss","../../scss/styles/avatar/avatar.scss","../../scss/styles/checkbox/checkbox.scss","../../scss/styles/checkbox/checkbox_base.scss","../../scss/styles/select/select.scss","../../scss/styles/select/select_base.scss","../../scss/styles/list/list_base.scss","../../scss/styles/typography/faces/ailerons_font_faces.scss","../../scss/styles/typography/faces/lato_font_faces.scss","../../scss/styles/typography/faces/roboto_font_faces.scss","../../scss/styles/typography/faces/raleway_font_faces.scss","../../scss/styles/typography/faces/pt_sans_font_faces.scss","../../scss/styles/success_label/success_label.scss","../../scss/styles/register/register.scss","../../scss/styles/animations.scss"],"names":[],"mappings":"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;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACdJ;ECCI;EACA;EACA;EACA,WANqB;EAOrB;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;EACA,WA3BiB;EA4BjB;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AChDZ;ECCI;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;;ACVJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;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;EAC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I;EACA;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;AAEA;EACI;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;;AAGA;EACI;;AAMhB;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;;;AAKZ;EACI;;;ACtHJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;EACA;;AAIR;EACI;;AAGJ;EACI;;;AC3CZ;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACdJ;ECCI;EACA;EACA;EACA,WANqB;EAOrB;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;EACA,WA3BiB;EA4BjB;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AChDZ;ECCI;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;;ACVJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;ACtBJ;EACI;EACA;EACA;EACA;;;ALNJ;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AZlBJ;EACI,WAPsB;EAQtB;EACA;EACA,eAN0B;EAO1B;;AAEA;EAPJ;IAQQ,YAXmB;;;;AAe3B;EACI;EACA;EACA,QAnBmB;EAoBnB,SArBoB;EAsBpB;EACA;;AAGI;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;AAEA;EACI;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;;AAGA;EACI;;AAMhB;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;;;AAKZ;EACI;;;AcvHA;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAIR;EACI;;;A5BdR;EACI;EACA;EACA,QALgB;EAMhB,SAPiB;EAQjB;;AAEA;EACI;EACA;EACA;;;A6BbR;EACI;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIN;EACI;EACA","file":"bundle.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/styles/footer/footer.scss","../../scss/styles/header/header.scss","../../scss/styles/button/button.scss","../../scss/styles/button/button_base.scss","../../scss/styles/input/input.scss","../../scss/styles/input/input_base.scss","../../scss/styles/label/label.scss","../../scss/styles/label/label_base.scss","../../scss/styles/radio/radio_base.scss","../../scss/styles/radio/radio.scss","../../scss/styles/a/a.scss","../../scss/styles/identity_provider/identity_provider.scss","../../scss/styles/identity_provider/identity_provider_base.scss","../../scss/styles/error/error.scss","../../scss/styles/qrcode/qrcode.scss","../../scss/styles/container/container.scss","../../scss/styles/account_selection/account_selection.scss","../../scss/styles/avatar/avatar.scss","../../scss/styles/checkbox/checkbox.scss","../../scss/styles/checkbox/checkbox_base.scss","../../scss/styles/select/select.scss","../../scss/styles/select/select_base.scss","../../scss/styles/list/list_base.scss","../../scss/styles/typography/faces/ailerons_font_faces.scss","../../scss/styles/typography/faces/lato_font_faces.scss","../../scss/styles/typography/faces/roboto_font_faces.scss","../../scss/styles/typography/faces/raleway_font_faces.scss","../../scss/styles/typography/faces/pt_sans_font_faces.scss","../../scss/styles/success_label/success_label.scss","../../scss/styles/register/register.scss","../../scss/styles/animations.scss"],"names":[],"mappings":";AAIA;EACI;EACA;EACA;EACA,YAPY;EAQZ;EACA;EACA,SATa;;AAWb;EACI;;AAGJ;EACI;EACA;;AAGJ;EAlBJ;IAmBQ;IACA;IACA;IACA;;;;ACvBR;EACI;EACA;EACA,QALgB;EAMhB,SAPiB;EAQjB;;AAEA;EACI;EACA;EACA;;;ACXR;ECkBE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;;AAEA;EACE;;AAGF;EACE;;;AD1CJ;ECcE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EAgBA;;AAdA;EACE;;AAGF;EACE;;;ADtCJ;EACE;EACA;EACA,OCCqB;EDArB;EACA;EACA,aCFqB;EDGrB,eCF8B;;ADI9B;EACE,aCJ0B;;;ADQ9B;EACE;EACA,SCf2B;EDgB3B,aCjB+B;;;ADoBjC;EACE;EACA,YC3B4B;;;AD+B5B;EACE;;;AEnCJ;AAAA;ECOI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADrBR;ECCI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;EACI,WAtB0B;EAuB1B;;;ADjBR;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AElBR;ECGI;EACA,WANkB;EAOlB;EACA,QAPe;EAQf,aAPoB;;;ACMxB;EACI;IACE;;EAGF;IACE;;EAGF;IACE;;;AChBN;EDqBI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA,QA9BS;EA+BT;EACA,SA7BkB;EA8BlB;EACA;EACA;EACA;EACA;EACA,WAzCc;EA0Cd;;AAEA;EAEI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA,OArDK;EAsDL,QAtDK;EAuDL;EACA;;AAGJ;EACI;EACA;EACA,OA7Da;EA8Db,QA9Da;EA+Db;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAOA;EACI;EACA;;AAGJ;EACI;;AAKZ;AAAA;AAAA;EAGI;;;AE5GR;EACI;EACA;EACA;EACA;;AAEA;EACI;;;ACLR;ECKI;EACA,QARa;EASb;EACA;EACA;EACA;EACA,SAZc;EAad,eAXoB;EAYpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAtB4B;EAuB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtCZ;EACI;EACA;EACA;;AACA;EACI;EACA;;;ACNR;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;;ACHR;EACI,WAPsB;EAQtB;EACA;EACA,eAN0B;EAO1B;;AAEA;EAPJ;IAQQ,YAXmB;;;;AAe3B;EACI;EACA;EACA,QAnBmB;EAoBnB,SArBoB;EAsBpB;EACA;;AAGI;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;AAEA;EACI;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;;AAGA;EACI;;AAMhB;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;;;AAKZ;EACI;;;ACtHJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAIR;EACI;;AAGJ;EACI;;;AChDZ;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACdJ;ECCI;EACA;EACA;EACA,WANqB;EAOrB;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;EACA,WA3BiB;EA4BjB;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AChDZ;ECCI;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;;ACVJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA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EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;AAEA;EACI;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;;AAGA;EACI;;AAMhB;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;;;AAKZ;EACI;;;ACtHJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAIR;EACI;;AAGJ;EACI;;;AChDZ;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACdJ;ECCI;EACA;EACA;EACA,WANqB;EAOrB;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;EACA,WA3BiB;EA4BjB;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AChDZ;ECCI;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;;ACVJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;ACtBJ;EACI;EACA;EACA;EACA;;;ALNJ;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AC9GJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AZlBJ;EACI,WAPsB;EAQtB;EACA;EACA,eAN0B;EAO1B;;AAEA;EAPJ;IAQQ,YAXmB;;;;AAe3B;EACI;EACA;EACA,QAnBmB;EAoBnB,SArBoB;EAsBpB;EACA;;AAGI;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;AAEA;EACI;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;;AAGA;EACI;;AAMhB;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;;;AAKZ;EACI;;;AcvHA;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAIR;EACI;;;A5BdR;EACI;EACA;EACA,QALgB;EAMhB,SAPiB;EAQjB;;AAEA;EACI;EACA;EACA;;;A6BbR;EACI;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;;;AAIN;EACI;EACA","file":"bundle.css"} \ No newline at end of file diff --git a/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css b/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css index 318ea5e852..b8feec19b7 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css +++ b/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css @@ -448,15 +448,20 @@ i { display: block; margin: 0.5rem; text-align: start; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-displayname { font-size: 16px; margin: 0.5rem 0; margin-bottom: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-loginname { font-size: 14px; margin: 0; + text-overflow: ellipsis; + overflow: hidden; } .lgn-account-selection .lgn-account .lgn-names .lgn-session-state { margin: 0; diff --git a/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css.map b/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css.map index 4731937075..4566893b68 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css.map +++ b/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../scss/styles/footer/footer.scss","../../scss/styles/header/header.scss","../../scss/styles/button/button.scss","../../scss/styles/button/button_base.scss","../../scss/styles/input/input.scss","../../scss/styles/input/input_base.scss","../../scss/styles/label/label.scss","../../scss/styles/label/label_base.scss","../../scss/styles/radio/radio_base.scss","../../scss/styles/radio/radio.scss","../../scss/styles/a/a.scss","../../scss/styles/identity_provider/identity_provider.scss","../../scss/styles/identity_provider/identity_provider_base.scss","../../scss/styles/error/error.scss","../../scss/styles/qrcode/qrcode.scss","../../scss/styles/container/container.scss","../../scss/styles/account_selection/account_selection.scss","../../scss/styles/avatar/avatar.scss","../../scss/styles/checkbox/checkbox.scss","../../scss/styles/checkbox/checkbox_base.scss","../../scss/styles/select/select.scss","../../scss/styles/select/select_base.scss","../../scss/styles/list/list_base.scss","../../scss/styles/typography/faces/ailerons_font_faces.scss","../../scss/styles/typography/faces/lato_font_faces.scss","../../scss/styles/typography/faces/roboto_font_faces.scss","../../scss/styles/typography/faces/raleway_font_faces.scss","../../scss/styles/typography/faces/pt_sans_font_faces.scss","../../scss/styles/success_label/success_label.scss","../../scss/styles/typography/typography.scss","../../scss/styles/core/core.scss","../../scss/styles/header/header_theme.scss","../../scss/styles/button/button_theme.scss","../../scss/styles/elevation/elevation.scss","../../scss/styles/input/input_theme.scss","../../scss/styles/radio/radio_theme.scss","../../scss/styles/checkbox/checkbox_theme.scss","../../scss/styles/label/label_theme.scss","../../scss/styles/footer/footer_theme.scss","../../scss/styles/a/a_theme.scss","../../scss/styles/error/error_theme.scss","../../scss/styles/qrcode/qrcode_theme.scss","../../scss/styles/container/container_theme.scss","../../scss/styles/account_selection/account_selection_theme.scss","../../scss/styles/avatar/avatar_theme.scss","../../scss/styles/select/select_theme.scss","../../scss/styles/list/list_theme.scss","../../scss/styles/identity_provider/identity_provider_theme.scss","../../scss/styles/success_label/success_label_theme.scss"],"names":[],"mappings":"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;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACdJ;ECCI;EACA;EACA;EACA,WANqB;EAOrB;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;EACA,WA3BiB;EA4BjB;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AChDZ;ECCI;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;;ACVJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;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;EAC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","file":"zitadel.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../scss/styles/footer/footer.scss","../../scss/styles/header/header.scss","../../scss/styles/button/button.scss","../../scss/styles/button/button_base.scss","../../scss/styles/input/input.scss","../../scss/styles/input/input_base.scss","../../scss/styles/label/label.scss","../../scss/styles/label/label_base.scss","../../scss/styles/radio/radio_base.scss","../../scss/styles/radio/radio.scss","../../scss/styles/a/a.scss","../../scss/styles/identity_provider/identity_provider.scss","../../scss/styles/identity_provider/identity_provider_base.scss","../../scss/styles/error/error.scss","../../scss/styles/qrcode/qrcode.scss","../../scss/styles/container/container.scss","../../scss/styles/account_selection/account_selection.scss","../../scss/styles/avatar/avatar.scss","../../scss/styles/checkbox/checkbox.scss","../../scss/styles/checkbox/checkbox_base.scss","../../scss/styles/select/select.scss","../../scss/styles/select/select_base.scss","../../scss/styles/list/list_base.scss","../../scss/styles/typography/faces/ailerons_font_faces.scss","../../scss/styles/typography/faces/lato_font_faces.scss","../../scss/styles/typography/faces/roboto_font_faces.scss","../../scss/styles/typography/faces/raleway_font_faces.scss","../../scss/styles/typography/faces/pt_sans_font_faces.scss","../../scss/styles/success_label/success_label.scss","../../scss/styles/typography/typography.scss","../../scss/styles/core/core.scss","../../scss/styles/header/header_theme.scss","../../scss/styles/button/button_theme.scss","../../scss/styles/elevation/elevation.scss","../../scss/styles/input/input_theme.scss","../../scss/styles/radio/radio_theme.scss","../../scss/styles/checkbox/checkbox_theme.scss","../../scss/styles/label/label_theme.scss","../../scss/styles/footer/footer_theme.scss","../../scss/styles/a/a_theme.scss","../../scss/styles/error/error_theme.scss","../../scss/styles/qrcode/qrcode_theme.scss","../../scss/styles/container/container_theme.scss","../../scss/styles/account_selection/account_selection_theme.scss","../../scss/styles/avatar/avatar_theme.scss","../../scss/styles/select/select_theme.scss","../../scss/styles/list/list_theme.scss","../../scss/styles/identity_provider/identity_provider_theme.scss","../../scss/styles/success_label/success_label_theme.scss"],"names":[],"mappings":";AAIA;EACI;EACA;EACA;EACA,YAPY;EAQZ;EACA;EACA,SATa;;AAWb;EACI;;AAGJ;EACI;EACA;;AAGJ;EAlBJ;IAmBQ;IACA;IACA;IACA;;;;ACvBR;EACI;EACA;EACA,QALgB;EAMhB,SAPiB;EAQjB;;AAEA;EACI;EACA;EACA;;;ACXR;ECkBE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;;AAEA;EACE;;AAGF;EACE;;;AD1CJ;ECcE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA,QAjCkB;EAkClB,WAnCqB;EAoCrB,aAlCuB;EAmCvB,SAtCmB;EAuCnB,eAnCyB;EAqCzB;EAgBA;;AAdA;EACE;;AAGF;EACE;;;ADtCJ;EACE;EACA;EACA,OCCqB;EDArB;EACA;EACA,aCFqB;EDGrB,eCF8B;;ADI9B;EACE,aCJ0B;;;ADQ9B;EACE;EACA,SCf2B;EDgB3B,aCjB+B;;;ADoBjC;EACE;EACA,YC3B4B;;;AD+B5B;EACE;;;AEnCJ;AAAA;ECOI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;AAAA;EACI,WAtB0B;EAuB1B;;;ADrBR;ECCI;EACA;EACA,sBAXsB;EAYtB;EACA;EACA;EACA;EACA,eAZsB;EAatB;EACA;EACA;EACA,cAfqB;EAgBrB,QAlBoB;EAmBpB,SArBgB;EAsBhB;EACA;EACA,QAvBe;;AAyBf;EACI,WAtB0B;EAuB1B;;;ADjBR;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;;;AElBR;ECGI;EACA,WANkB;EAOlB;EACA,QAPe;EAQf,aAPoB;;;ACMxB;EACI;IACE;;EAGF;IACE;;EAGF;IACE;;;AChBN;EDqBI;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA,QA9BS;EA+BT;EACA,SA7BkB;EA8BlB;EACA;EACA;EACA;EACA;EACA,WAzCc;EA0Cd;;AAEA;EAEI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA,OArDK;EAsDL,QAtDK;EAuDL;EACA;;AAGJ;EACI;EACA;EACA,OA7Da;EA8Db,QA9Da;EA+Db;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAOA;EACI;EACA;;AAGJ;EACI;;AAKZ;AAAA;AAAA;EAGI;;;AE5GR;EACI;EACA;EACA;EACA;;AAEA;EACI;;;ACLR;ECKI;EACA,QARa;EASb;EACA;EACA;EACA;EACA,SAZc;EAad,eAXoB;EAYpB;EACA;;AAEA;EACI;EACA;;AAGJ;EACI,aAtB4B;EAuB5B;EACA;EACA;;AAIA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;;;ACtCZ;EACI;EACA;EACA;;AACA;EACI;EACA;;;ACNR;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;;;ACHR;EACI,WAPsB;EAQtB;EACA;EACA,eAN0B;EAO1B;;AAEA;EAPJ;IAQQ,YAXmB;;;;AAe3B;EACI;EACA;EACA,QAnBmB;EAoBnB,SArBoB;EAsBpB;EACA;;AAGI;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;EACA;EACA;;AAIA;EACI;;AAGJ;EACI;;AAIR;EACI;;AAEA;EACI;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAGJ;EACI;;AAGA;EACI;;AAMhB;EACI;EACA;EACA;;AAGJ;EACI;EACA;;AAGJ;EACI;EACA;;AAEA;EACI;EACA;;;AAKZ;EACI;;;ACtHJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;;AAEA;EACI;;AAGJ;EACI;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;;AAIR;EACI;;AAGJ;EACI;;;AChDZ;EACI,QAHc;EAId,OAJc;EAKd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;ACdJ;ECCI;EACA;EACA;EACA,WANqB;EAOrB;EACA;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACI;EACA;EACA;EACA;EACA,WA3BiB;EA4BjB;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AChDZ;ECCI;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;;ACVJ;EACI;EACA;;AAEA;EACI;;AAIR;EACI;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAIR;EACI;EACA;;;ACpCR;EACI;EACA;;ACFJ;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;;AC7DJ;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;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;ACzEJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA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","file":"zitadel.css"} \ No newline at end of file diff --git a/internal/ui/login/static/templates/link_users_done.html b/internal/ui/login/static/templates/link_users_done.html index af1922b163..5b5a76b050 100644 --- a/internal/ui/login/static/templates/link_users_done.html +++ b/internal/ui/login/static/templates/link_users_done.html @@ -17,7 +17,7 @@ {{t "Actions.Cancel"}} - + diff --git a/internal/user/model/machine_key.go b/internal/user/model/machine_key.go deleted file mode 100644 index c38aa1b630..0000000000 --- a/internal/user/model/machine_key.go +++ /dev/null @@ -1,54 +0,0 @@ -package model - -import ( - "time" - - "github.com/caos/zitadel/internal/model" -) - -type MachineKeyView struct { - ID string - UserID string - Type MachineKeyType - Sequence uint64 - CreationDate time.Time - ExpirationDate time.Time - PublicKey []byte -} - -type MachineKeySearchRequest struct { - Offset uint64 - Limit uint64 - SortingColumn MachineKeySearchKey - Asc bool - Queries []*MachineKeySearchQuery -} - -type MachineKeySearchKey int32 - -const ( - MachineKeyKeyUnspecified MachineKeySearchKey = iota - MachineKeyKeyID - MachineKeyKeyUserID -) - -type MachineKeySearchQuery struct { - Key MachineKeySearchKey - Method model.SearchMethod - Value interface{} -} - -type MachineKeySearchResponse struct { - Offset uint64 - Limit uint64 - TotalResult uint64 - Result []*MachineKeyView - Sequence uint64 - Timestamp time.Time -} - -func (r *MachineKeySearchRequest) EnsureLimit(limit uint64) { - if r.Limit == 0 || r.Limit > limit { - r.Limit = limit - } -} diff --git a/internal/user/model/user_machine.go b/internal/user/model/user_machine.go index d635f81506..1f1c06a696 100644 --- a/internal/user/model/user_machine.go +++ b/internal/user/model/user_machine.go @@ -4,6 +4,7 @@ import ( "time" "github.com/caos/zitadel/internal/eventstore/models" + key_model "github.com/caos/zitadel/internal/key/model" ) type Machine struct { @@ -21,14 +22,7 @@ type MachineKey struct { models.ObjectRoot KeyID string - Type MachineKeyType + Type key_model.AuthNKeyType ExpirationDate time.Time PrivateKey []byte } - -type MachineKeyType int32 - -const ( - MachineKeyTypeNONE = iota - MachineKeyTypeJSON -) diff --git a/internal/user/repository/eventsourcing/eventstore.go b/internal/user/repository/eventsourcing/eventstore.go index 2369642565..7beeef6b1c 100644 --- a/internal/user/repository/eventsourcing/eventstore.go +++ b/internal/user/repository/eventsourcing/eventstore.go @@ -20,6 +20,7 @@ import ( es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/id" + key_model "github.com/caos/zitadel/internal/key/model" global_model "github.com/caos/zitadel/internal/model" "github.com/caos/zitadel/internal/telemetry/tracing" usr_model "github.com/caos/zitadel/internal/user/model" @@ -27,11 +28,6 @@ import ( webauthn_helper "github.com/caos/zitadel/internal/webauthn" ) -const ( - yearLayout = "2006-01-02" - defaultExpirationDate = "9999-01-01" -) - type UserEventstore struct { es_int.Eventstore userCache *UserCache @@ -1638,7 +1634,7 @@ func (es *UserEventstore) AddMachineKey(ctx context.Context, key *usr_model.Mach } if key.ExpirationDate.IsZero() { - key.ExpirationDate, err = time.Parse(yearLayout, defaultExpirationDate) + key.ExpirationDate, err = key_model.DefaultExpiration() if err != nil { logging.Log("EVENT-vzibi").WithError(err).Warn("unable to set default date") return nil, errors.ThrowInternal(err, "EVENT-j68fg", "Errors.Internal") diff --git a/internal/user/repository/eventsourcing/model/user_machine.go b/internal/user/repository/eventsourcing/model/user_machine.go index f73c75c5f0..a82fad1c1b 100644 --- a/internal/user/repository/eventsourcing/model/user_machine.go +++ b/internal/user/repository/eventsourcing/model/user_machine.go @@ -9,6 +9,7 @@ import ( "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models" + key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/user/model" ) @@ -115,7 +116,7 @@ func MachineKeyToModel(machine *MachineKey) *model.MachineKey { ExpirationDate: machine.ExpirationDate, KeyID: machine.KeyID, PrivateKey: machine.privateKey, - Type: model.MachineKeyType(machine.Type), + Type: key_model.AuthNKeyType(machine.Type), } } diff --git a/internal/user/repository/view/machine_key_view.go b/internal/user/repository/view/machine_key_view.go deleted file mode 100644 index 479859eab4..0000000000 --- a/internal/user/repository/view/machine_key_view.go +++ /dev/null @@ -1,77 +0,0 @@ -package view - -import ( - caos_errs "github.com/caos/zitadel/internal/errors" - global_model "github.com/caos/zitadel/internal/model" - usr_model "github.com/caos/zitadel/internal/user/model" - "github.com/caos/zitadel/internal/user/repository/view/model" - "github.com/caos/zitadel/internal/view/repository" - "github.com/jinzhu/gorm" -) - -func MachineKeyByIDs(db *gorm.DB, table, userID, keyID string) (*model.MachineKeyView, error) { - key := new(model.MachineKeyView) - query := repository.PrepareGetByQuery(table, - model.MachineKeySearchQuery{Key: usr_model.MachineKeyKeyUserID, Method: global_model.SearchMethodEquals, Value: userID}, - model.MachineKeySearchQuery{Key: usr_model.MachineKeyKeyID, Method: global_model.SearchMethodEquals, Value: keyID}, - ) - err := query(db, key) - if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-3Dk9s", "Errors.User.KeyNotFound") - } - return key, err -} - -func SearchMachineKeys(db *gorm.DB, table string, req *usr_model.MachineKeySearchRequest) ([]*model.MachineKeyView, uint64, error) { - members := make([]*model.MachineKeyView, 0) - query := repository.PrepareSearchQuery(table, model.MachineKeySearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries}) - count, err := query(db, &members) - if err != nil { - return nil, 0, err - } - return members, count, nil -} - -func MachineKeysByUserID(db *gorm.DB, table string, userID string) ([]*model.MachineKeyView, error) { - keys := make([]*model.MachineKeyView, 0) - queries := []*usr_model.MachineKeySearchQuery{ - { - Key: usr_model.MachineKeyKeyUserID, - Value: userID, - Method: global_model.SearchMethodEquals, - }, - } - query := repository.PrepareSearchQuery(table, model.MachineKeySearchRequest{Queries: queries}) - _, err := query(db, &keys) - if err != nil { - return nil, err - } - return keys, nil -} - -func MachineKeyByID(db *gorm.DB, table string, keyID string) (*model.MachineKeyView, error) { - key := new(model.MachineKeyView) - query := repository.PrepareGetByQuery(table, - model.MachineKeySearchQuery{Key: usr_model.MachineKeyKeyID, Method: global_model.SearchMethodEquals, Value: keyID}, - ) - err := query(db, key) - if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-BjN6x", "Errors.User.KeyNotFound") - } - return key, err -} - -func PutMachineKey(db *gorm.DB, table string, role *model.MachineKeyView) error { - save := repository.PrepareSave(table) - return save(db, role) -} - -func DeleteMachineKey(db *gorm.DB, table, keyID string) error { - delete := repository.PrepareDeleteByKey(table, model.MachineKeySearchKey(usr_model.MachineKeyKeyID), keyID) - return delete(db) -} - -func DeleteMachineKeysByUserID(db *gorm.DB, table, userID string) error { - delete := repository.PrepareDeleteByKey(table, model.MachineKeySearchKey(usr_model.MachineKeyKeyUserID), userID) - return delete(db) -} diff --git a/internal/user/repository/view/model/machine_key.go b/internal/user/repository/view/model/machine_key.go deleted file mode 100644 index ddd921d599..0000000000 --- a/internal/user/repository/view/model/machine_key.go +++ /dev/null @@ -1,84 +0,0 @@ -package model - -import ( - "encoding/json" - "time" - - "github.com/caos/logging" - - caos_errs "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/user/model" - es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" -) - -const ( - MachineKeyKeyID = "id" - MachineKeyKeyUserID = "user_id" -) - -type MachineKeyView struct { - ID string `json:"keyId" gorm:"column:id;primary_key"` - UserID string `json:"-" gorm:"column:user_id;primary_key"` - Type int32 `json:"type" gorm:"column:machine_type"` - ExpirationDate time.Time `json:"expirationDate" gorm:"column:expiration_date"` - Sequence uint64 `json:"-" gorm:"column:sequence"` - - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - - PublicKey []byte `json:"publicKey" gorm:"column:public_key"` -} - -func MachineKeyViewFromModel(key *model.MachineKeyView) *MachineKeyView { - return &MachineKeyView{ - ID: key.ID, - UserID: key.UserID, - Type: int32(key.Type), - ExpirationDate: key.ExpirationDate, - Sequence: key.Sequence, - CreationDate: key.CreationDate, - } -} - -func MachineKeyToModel(key *MachineKeyView) *model.MachineKeyView { - return &model.MachineKeyView{ - ID: key.ID, - UserID: key.UserID, - Type: model.MachineKeyType(key.Type), - ExpirationDate: key.ExpirationDate, - Sequence: key.Sequence, - CreationDate: key.CreationDate, - PublicKey: key.PublicKey, - } -} - -func MachineKeysToModel(keys []*MachineKeyView) []*model.MachineKeyView { - result := make([]*model.MachineKeyView, len(keys)) - for i, key := range keys { - result[i] = MachineKeyToModel(key) - } - return result -} - -func (k *MachineKeyView) AppendEvent(event *models.Event) (err error) { - k.Sequence = event.Sequence - switch event.Type { - case es_model.MachineKeyAdded: - k.setRootData(event) - k.CreationDate = event.CreationDate - err = k.SetData(event) - } - return err -} - -func (k *MachineKeyView) setRootData(event *models.Event) { - k.UserID = event.AggregateID -} - -func (r *MachineKeyView) SetData(event *models.Event) error { - if err := json.Unmarshal(event.Data, r); err != nil { - logging.Log("EVEN-Sj90d").WithError(err).Error("could not unmarshal event data") - return caos_errs.ThrowInternal(err, "MODEL-lub6s", "Could not unmarshal data") - } - return nil -} diff --git a/internal/user/repository/view/model/machine_key_query.go b/internal/user/repository/view/model/machine_key_query.go deleted file mode 100644 index c5a876ce53..0000000000 --- a/internal/user/repository/view/model/machine_key_query.go +++ /dev/null @@ -1,61 +0,0 @@ -package model - -import ( - global_model "github.com/caos/zitadel/internal/model" - usr_model "github.com/caos/zitadel/internal/user/model" - "github.com/caos/zitadel/internal/view/repository" -) - -type MachineKeySearchRequest usr_model.MachineKeySearchRequest -type MachineKeySearchQuery usr_model.MachineKeySearchQuery -type MachineKeySearchKey usr_model.MachineKeySearchKey - -func (req MachineKeySearchRequest) GetLimit() uint64 { - return req.Limit -} - -func (req MachineKeySearchRequest) GetOffset() uint64 { - return req.Offset -} - -func (req MachineKeySearchRequest) GetSortingColumn() repository.ColumnKey { - if req.SortingColumn == usr_model.MachineKeyKeyUnspecified { - return nil - } - return MachineKeySearchKey(req.SortingColumn) -} - -func (req MachineKeySearchRequest) GetAsc() bool { - return req.Asc -} - -func (req MachineKeySearchRequest) GetQueries() []repository.SearchQuery { - result := make([]repository.SearchQuery, len(req.Queries)) - for i, q := range req.Queries { - result[i] = MachineKeySearchQuery{Key: q.Key, Value: q.Value, Method: q.Method} - } - return result -} - -func (req MachineKeySearchQuery) GetKey() repository.ColumnKey { - return MachineKeySearchKey(req.Key) -} - -func (req MachineKeySearchQuery) GetMethod() global_model.SearchMethod { - return req.Method -} - -func (req MachineKeySearchQuery) GetValue() interface{} { - return req.Value -} - -func (key MachineKeySearchKey) ToColumnName() string { - switch usr_model.MachineKeySearchKey(key) { - case usr_model.MachineKeyKeyID: - return MachineKeyKeyID - case usr_model.MachineKeyKeyUserID: - return MachineKeyKeyUserID - default: - return "" - } -} diff --git a/internal/user/repository/view/model/token.go b/internal/user/repository/view/model/token.go index eb20cf76c9..24906d2a56 100644 --- a/internal/user/repository/view/model/token.go +++ b/internal/user/repository/view/model/token.go @@ -5,6 +5,7 @@ import ( "time" "github.com/caos/logging" + caos_errs "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/v2/command/setup_step11.go b/internal/v2/command/setup_step11.go index ec47cc131b..9b70533cc9 100644 --- a/internal/v2/command/setup_step11.go +++ b/internal/v2/command/setup_step11.go @@ -22,12 +22,16 @@ func (s *Step11) execute(ctx context.Context, commandSide *CommandSide) error { func (r *CommandSide) SetupStep11(ctx context.Context, step *Step11) error { fn := func(iam *IAMWriteModel) (*iam_repo.Aggregate, error) { iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel) - uniqueConstraints := NewUniqueConstraintReadModel(ctx, r) - err := r.eventstore.FilterToQueryReducer(ctx, uniqueConstraints) - if err != nil { - return nil, err + var uniqueContraintMigrations []*domain.UniqueConstraintMigration + if step.MigrateV1EventstoreToV2 { + uniqueConstraints := NewUniqueConstraintReadModel(ctx, r) + err := r.eventstore.FilterToQueryReducer(ctx, uniqueConstraints) + if err != nil { + return nil, err + } + uniqueContraintMigrations = uniqueConstraints.UniqueConstraints } - iamAgg.PushEvents(iam_repo.NewMigrateUniqueConstraintEvent(ctx, uniqueConstraints.UniqueConstraints)) + iamAgg.PushEvents(iam_repo.NewMigrateUniqueConstraintEvent(ctx, uniqueContraintMigrations)) logging.Log("SETUP-M9fsd").Info("migrate v1 eventstore to v2") return iamAgg, nil } diff --git a/internal/v2/command/unique_constraints_model.go b/internal/v2/command/unique_constraints_model.go index c61f8f4d81..0c5bf3a9e9 100644 --- a/internal/v2/command/unique_constraints_model.go +++ b/internal/v2/command/unique_constraints_model.go @@ -45,7 +45,7 @@ func (rm *UniqueConstraintReadModel) Reduce() error { rm.addUniqueConstraint(e.AggregateID(), e.AggregateID(), org.NewAddOrgNameUniqueConstraint(e.Name)) case *org.OrgChangedEvent: rm.changeUniqueConstraint(e.AggregateID(), e.AggregateID(), org.NewAddOrgNameUniqueConstraint(e.Name)) - case *org.DomainVerificationAddedEvent: + case *org.DomainVerifiedEvent: rm.addUniqueConstraint(e.AggregateID(), e.AggregateID(), org.NewAddOrgNameUniqueConstraint(e.Domain)) case *org.DomainRemovedEvent: rm.removeUniqueConstraint(e.AggregateID(), e.AggregateID(), org.UniqueOrgDomain) @@ -158,9 +158,7 @@ func (rm *UniqueConstraintReadModel) Reduce() error { } func (rm *UniqueConstraintReadModel) Query() *eventstore.SearchQueryBuilder { - return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AggregateIDs(rm.AggregateID). - ResourceOwner(rm.ResourceOwner). + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType, org.AggregateType, project.AggregateType, user.AggregateType, usergrant.AggregateType). EventTypes( org.OrgAddedEventType, org.OrgChangedEventType, diff --git a/internal/v2/domain/application_oidc.go b/internal/v2/domain/application_oidc.go index b652b8f2b6..b5c6cf54bb 100644 --- a/internal/v2/domain/application_oidc.go +++ b/internal/v2/domain/application_oidc.go @@ -90,6 +90,7 @@ const ( OIDCAuthMethodTypeBasic OIDCAuthMethodType = iota OIDCAuthMethodTypePost OIDCAuthMethodTypeNone + OIDCAuthMethodTypePrivateKeyJWT ) type Compliance struct { diff --git a/migrations/cockroach/V1.29__user_memberships.sql b/migrations/cockroach/V1.29__user_memberships.sql new file mode 100644 index 0000000000..30d4259ce9 --- /dev/null +++ b/migrations/cockroach/V1.29__user_memberships.sql @@ -0,0 +1,18 @@ +CREATE TABLE authz.user_memberships ( + user_id TEXT, + member_type SMALLINT, + aggregate_id TEXT, + object_id TEXT, + + roles TEXT ARRAY, + display_name TEXT, + resource_owner TEXT, + resource_owner_name TEXT, + creation_date TIMESTAMPTZ, + change_date TIMESTAMPTZ, + sequence BIGINT, + + PRIMARY KEY (user_id, member_type, aggregate_id, object_id) +); + +ALTER TABLE authz.user_memberships OWNER TO admin; \ No newline at end of file diff --git a/migrations/cockroach/V1.30__user_memberships.sql b/migrations/cockroach/V1.30__user_memberships.sql new file mode 100644 index 0000000000..82146e0f18 --- /dev/null +++ b/migrations/cockroach/V1.30__user_memberships.sql @@ -0,0 +1,47 @@ +BEGIN; + +TRUNCATE TABLE authz.user_memberships; + +INSERT INTO authz.user_memberships ( + user_id, + member_type, + aggregate_id, + object_id, + roles, + display_name, + resource_owner, + resource_owner_name, + creation_date, + change_date, + sequence +) +SELECT + user_id, + member_type, + aggregate_id, + object_id, + roles, + display_name, + resource_owner, + resource_owner_name, + creation_date, + change_date, + sequence +FROM auth.user_memberships; + +UPSERT INTO authz.current_sequences ( + view_name, + event_timestamp, + current_sequence, + last_successful_spooler_run, + aggregate_type + ) +SELECT 'authz.user_memberships', + event_timestamp, + current_sequence, + last_successful_spooler_run, + aggregate_type +FROM auth.current_sequences +WHERE view_name = 'auth.user_memberships'; + +COMMIT; \ No newline at end of file diff --git a/migrations/cockroach/V1.31__auth_keys.sql b/migrations/cockroach/V1.31__auth_keys.sql new file mode 100644 index 0000000000..5c091d1b50 --- /dev/null +++ b/migrations/cockroach/V1.31__auth_keys.sql @@ -0,0 +1,92 @@ +CREATE TABLE auth.authn_keys +( + key_id TEXT, + object_id TEXT, + object_type SMALLINT, + auth_identifier TEXT, + + key_type SMALLINT, + sequence BIGINT, + expiration_date TIMESTAMPTZ, + creation_date TIMESTAMPTZ, + public_key BYTES, + state SMALLINT, + + PRIMARY KEY (key_id, object_id, object_type, auth_identifier) +); + +INSERT INTO auth.authn_keys ( + key_id, + object_id, + object_type, + auth_identifier, + key_type, + sequence, + expiration_date, + creation_date, + public_key, + state + ) + SELECT + id, + user_id, + 0, + user_id, + machine_type, + sequence, + expiration_date, + creation_date, + public_key, + 0 + FROM auth.machine_keys; + +CREATE TABLE management.authn_keys +( + key_id TEXT, + object_id TEXT, + object_type SMALLINT, + auth_identifier TEXT, + + key_type SMALLINT, + sequence BIGINT, + expiration_date TIMESTAMPTZ, + creation_date TIMESTAMPTZ, + public_key BYTES, + state SMALLINT, + + PRIMARY KEY (key_id, object_id, object_type, auth_identifier) +); + +INSERT INTO management.authn_keys ( + key_id, + object_id, + object_type, + auth_identifier, + key_type, + sequence, + expiration_date, + creation_date, + public_key, + state +) +SELECT + id, + user_id, + 0, + user_id, + machine_type, + sequence, + expiration_date, + creation_date, + public_key, + 0 +FROM management.machine_keys; + +INSERT INTO auth.current_sequences (view_name, event_timestamp, current_sequence, last_successful_spooler_run, aggregate_type) + SELECT 'auth.authn_keys', event_timestamp, current_sequence, last_successful_spooler_run, aggregate_type FROM auth.current_sequences WHERE view_name = 'auth.machine_keys'; + +INSERT INTO management.current_sequences (view_name, event_timestamp, current_sequence, last_successful_spooler_run, aggregate_type) + SELECT 'management.authn_keys', event_timestamp, current_sequence, last_successful_spooler_run, aggregate_type FROM management.current_sequences WHERE view_name = 'management.machine_keys'; + +ALTER TABLE auth.authn_keys OWNER TO admin; +ALTER TABLE management.authn_keys OWNER TO admin; diff --git a/migrations/cockroach/V1.29__unique_tables.sql b/migrations/cockroach/V1.32__unique_tables.sql similarity index 100% rename from migrations/cockroach/V1.29__unique_tables.sql rename to migrations/cockroach/V1.32__unique_tables.sql diff --git a/migrations/cockroach/V1.30__policies.sql b/migrations/cockroach/V1.33__policies.sql similarity index 100% rename from migrations/cockroach/V1.30__policies.sql rename to migrations/cockroach/V1.33__policies.sql diff --git a/pkg/grpc/auth/mock/auth.proto.mock.go b/pkg/grpc/auth/mock/auth.proto.mock.go index 97405e6fdd..6f6dd61363 100644 --- a/pkg/grpc/auth/mock/auth.proto.mock.go +++ b/pkg/grpc/auth/mock/auth.proto.mock.go @@ -636,6 +636,26 @@ func (mr *MockAuthServiceClientMockRecorder) SearchMyUserGrant(arg0, arg1 interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchMyUserGrant", reflect.TypeOf((*MockAuthServiceClient)(nil).SearchMyUserGrant), varargs...) } +// SearchMyUserMemberships mocks base method +func (m *MockAuthServiceClient) SearchMyUserMemberships(arg0 context.Context, arg1 *auth.UserMembershipSearchRequest, arg2 ...grpc.CallOption) (*auth.UserMembershipSearchResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SearchMyUserMemberships", varargs...) + ret0, _ := ret[0].(*auth.UserMembershipSearchResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SearchMyUserMemberships indicates an expected call of SearchMyUserMemberships +func (mr *MockAuthServiceClientMockRecorder) SearchMyUserMemberships(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchMyUserMemberships", reflect.TypeOf((*MockAuthServiceClient)(nil).SearchMyUserMemberships), varargs...) +} + // UpdateMyUserAddress mocks base method func (m *MockAuthServiceClient) UpdateMyUserAddress(arg0 context.Context, arg1 *auth.UpdateUserAddressRequest, arg2 ...grpc.CallOption) (*auth.UserAddress, error) { m.ctrl.T.Helper() diff --git a/pkg/grpc/auth/proto/auth.proto b/pkg/grpc/auth/proto/auth.proto index 645a03af68..bb5ca291ae 100644 --- a/pkg/grpc/auth/proto/auth.proto +++ b/pkg/grpc/auth/proto/auth.proto @@ -418,6 +418,17 @@ service AuthService { permission: "authenticated" }; } + + rpc SearchMyUserMemberships(UserMembershipSearchRequest) returns (UserMembershipSearchResponse) { + option (google.api.http) = { + post: "/users/me/memberships/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "authenticated" + }; + } } message UserSessionViews { @@ -856,4 +867,52 @@ message ExternalIDPView { string external_user_display_name = 5; google.protobuf.Timestamp creation_date = 6; google.protobuf.Timestamp change_date = 7; -} \ No newline at end of file +} + + +message UserMembershipSearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated UserMembershipView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + +message UserMembershipSearchRequest { + uint64 offset = 1; + uint64 limit = 2; + repeated UserMembershipSearchQuery queries = 3; +} + +message UserMembershipSearchQuery { + UserMembershipSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}]; + SearchMethod method = 2 [(validate.rules).enum = {in: [0]}]; + string value = 3; +} + +enum UserMembershipSearchKey { + USERMEMBERSHIPSEARCHKEY_UNSPECIFIED = 0; + USERMEMBERSHIPSEARCHKEY_TYPE = 1; + USERMEMBERSHIPSEARCHKEY_OBJECT_ID = 2; +} + +message UserMembershipView { + string user_id = 1; + MemberType member_type = 2; + string aggregate_id = 3; + string object_id = 4; + repeated string roles = 5; + string display_name = 6; + google.protobuf.Timestamp creation_date = 7; + google.protobuf.Timestamp change_date = 8; + uint64 sequence = 9; + string resource_owner = 10; +} + +enum MemberType { + MEMBERTYPE_UNSPECIFIED = 0; + MEMBERTYPE_ORGANISATION = 1; + MEMBERTYPE_PROJECT = 2; + MEMBERTYPE_PROJECT_GRANT = 3; +} diff --git a/pkg/grpc/management/proto/management.proto b/pkg/grpc/management/proto/management.proto index 3f854432b1..7cf1e86332 100644 --- a/pkg/grpc/management/proto/management.proto +++ b/pkg/grpc/management/proto/management.proto @@ -778,6 +778,7 @@ service ManagementService { option (caos.zitadel.utils.v1.auth_option) = { permission: "project.read" + check_field_name: "Id" }; } @@ -945,6 +946,18 @@ service ManagementService { }; } + rpc CreateAPIApplication(APIApplicationCreate) returns (Application) { + option (google.api.http) = { + post: "/projects/{project_id}/applications/api" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + rpc UpdateApplication(ApplicationUpdate) returns (Application) { option (google.api.http) = { put: "/projects/{project_id}/applications/{id}" @@ -1016,6 +1029,76 @@ service ManagementService { }; } + rpc UpdateApplicationAPIConfig(APIConfigUpdate) returns (APIConfig) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{application_id}/apiconfig" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc RegenerateAPIClientSecret(ApplicationID) returns (ClientSecret) { + option (google.api.http) = { + put: "/projects/{project_id}/applications/{id}/apiconfig/_changeclientsecret" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc AddClientKey(AddClientKeyRequest) returns (AddClientKeyResponse){ + option (google.api.http) = { + post: "/projects/{project_id}/applications/{application_id}/keys" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc DeleteClientKey(ClientKeyIDRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/projects/{project_id}/applications/{application_id}/keys/{key_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.write" + check_field_name: "ProjectId" + }; + } + + rpc SearchClientKeys(ClientKeySearchRequest) returns (ClientKeySearchResponse) { + option (google.api.http) = { + post: "/projects/{project_id}/applications/{application_id}/keys/_search" + body: "*" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.read" + check_field_name: "ProjectId" + }; + } + + rpc GetClientKey(ClientKeyIDRequest) returns (ClientKeyView) { + option (google.api.http) = { + get: "/projects/{project_id}/applications/{application_id}/keys/{key_id}" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "project.app.read" + check_field_name: "ProjectId" + }; + } + rpc SearchProjectGrants(ProjectGrantSearchRequest) returns (ProjectGrantSearchResponse) { option (google.api.http) = { post: "/projects/{project_id}/grants/_search" @@ -1409,7 +1492,7 @@ service ManagementService { }; option (caos.zitadel.utils.v1.auth_option) = { - permission: "iam.policy.read" + permission: "policy.read" }; } @@ -1420,7 +1503,7 @@ service ManagementService { }; option (caos.zitadel.utils.v1.auth_option) = { - permission: "iam.policy.write" + permission: "policy.write" }; } @@ -1430,7 +1513,7 @@ service ManagementService { }; option (caos.zitadel.utils.v1.auth_option) = { - permission: "iam.policy.write" + permission: "policy.write" }; } @@ -1440,7 +1523,7 @@ service ManagementService { }; option (caos.zitadel.utils.v1.auth_option) = { - permission: "iam.policy.read" + permission: "policy.read" }; } @@ -1451,7 +1534,7 @@ service ManagementService { }; option (caos.zitadel.utils.v1.auth_option) = { - permission: "iam.policy.write" + permission: "policy.write" }; } @@ -1461,7 +1544,7 @@ service ManagementService { }; option (caos.zitadel.utils.v1.auth_option) = { - permission: "iam.policy.write" + permission: "policy.write" }; } @@ -1673,7 +1756,7 @@ service ManagementService { }; } - + rpc GetMailTexts(google.protobuf.Empty) returns (MailTextsView) { option (google.api.http) = { get: "/orgs/me/policies/mailtexts" @@ -1725,7 +1808,7 @@ service ManagementService { permission: "policy.delete" }; } - + } message ZitadelDocs { @@ -2635,6 +2718,7 @@ message Application { string name = 5; oneof app_config { OIDCConfig oidc_config = 8; + APIConfig api_config = 10; } uint64 sequence = 9; } @@ -2683,6 +2767,18 @@ message OIDCApplicationCreate { google.protobuf.Duration clock_skew = 15 [(validate.rules).duration = {gte: {}, lte: {seconds: 5}}]; } +message APIApplicationCreate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + APIAuthMethodType auth_method_type = 3; +} + +message APIConfig { + string client_id = 1; + string client_secret = 2; + APIAuthMethodType auth_method_type = 3; +} + enum OIDCVersion { OIDCV1_0 = 0; } @@ -2709,6 +2805,12 @@ message OIDCConfigUpdate { google.protobuf.Duration clock_skew = 14 [(validate.rules).duration = {gte: {}, lte: {seconds: 5}}]; } +message APIConfigUpdate { + string project_id = 1 [(validate.rules).string = {min_len: 1}]; + string application_id = 2 [(validate.rules).string = {min_len: 1}]; + APIAuthMethodType auth_method_type = 7; +} + enum OIDCResponseType { OIDCRESPONSETYPE_CODE = 0; OIDCRESPONSETYPE_ID_TOKEN = 1; @@ -2731,6 +2833,12 @@ enum OIDCAuthMethodType { OIDCAUTHMETHODTYPE_BASIC = 0; OIDCAUTHMETHODTYPE_POST = 1; OIDCAUTHMETHODTYPE_NONE = 2; + OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT = 3; +} + +enum APIAuthMethodType { + APIAUTHMETHODTYPE_BASIC = 0; + APIAUTHMETHODTYPE_PRIVATE_KEY_JWT = 1; } message ClientSecret { @@ -2777,6 +2885,60 @@ enum ApplicationSearchKey { APPLICATIONSEARCHKEY_APP_NAME = 1; } +message AddClientKeyRequest { + string project_id = 1 [(validate.rules).string.min_len = 1]; + string application_id = 2 [(validate.rules).string.min_len = 1]; + AuthNKeyType type = 3 [(validate.rules).enum = {not_in: [0]}]; + google.protobuf.Timestamp expiration_date = 4; +} + +message AddClientKeyResponse { + string id = 1; + google.protobuf.Timestamp creation_date = 2; + uint64 sequence = 3; + + AuthNKeyType type = 4; + google.protobuf.Timestamp expiration_date = 5; + bytes key_details = 6; +} + +message ClientKeyIDRequest { + string project_id = 1 [(validate.rules).string.min_len = 1]; + string application_id = 2 [(validate.rules).string.min_len = 1]; + string key_id = 3 [(validate.rules).string.min_len = 1]; +} + +message ClientKeyView { + string id = 1; + AuthNKeyType type = 2; + uint64 sequence = 3; + + google.protobuf.Timestamp creation_date = 4; + google.protobuf.Timestamp expiration_date = 5; +} + +enum AuthNKeyType { + AUTHNKEY_UNSPECIFIED = 0; + AUTHNKEY_JSON = 1; +} + +message ClientKeySearchRequest { + uint64 offset = 1; + uint64 limit = 2; + bool asc = 3; + string project_id = 4 [(validate.rules).string.min_len = 1]; + string application_id = 5 [(validate.rules).string.min_len = 1]; +} + +message ClientKeySearchResponse { + uint64 offset = 1; + uint64 limit = 2; + uint64 total_result = 3; + repeated ClientKeyView result = 4; + uint64 processed_sequence = 5; + google.protobuf.Timestamp view_timestamp = 6; +} + message ProjectGrant { string id = 1; string project_id = 2; @@ -3451,7 +3613,7 @@ message MailText { google.protobuf.Timestamp change_date = 10; } -message MailTextUpdate { +message MailTextUpdate { string mail_text_type = 1; string language = 2; string title = 3; @@ -3462,7 +3624,7 @@ message MailTextUpdate { string button_text = 8; } -message MailTextRemove { +message MailTextRemove { string mail_text_type = 1; string language = 2; } diff --git a/site/config.js b/site/config.js index e2ad88aec8..9a3d24fdb2 100644 --- a/site/config.js +++ b/site/config.js @@ -1,4 +1,4 @@ export const SLUG_PRESERVE_UNICODE = false; export const SLUG_SEPARATOR = '_'; export const SLUG_LANG = 'en'; -export const LANGUAGES = ['de', 'en']; +export const LANGUAGES = ['en']; diff --git a/site/docs/administrate/00-overview.de.md b/site/docs/administrate/00-overview.de.md deleted file mode 100644 index 43cac2931e..0000000000 --- a/site/docs/administrate/00-overview.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Übersicht ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/00-overview.en.md b/site/docs/administrate/00-overview.md similarity index 88% rename from site/docs/administrate/00-overview.en.md rename to site/docs/administrate/00-overview.md index a7ed644984..d03f204a6d 100644 --- a/site/docs/administrate/00-overview.en.md +++ b/site/docs/administrate/00-overview.md @@ -2,6 +2,8 @@ title: Overview --- +This documentation describes the structure of ZITADEL, its role system and in parallel the use of [console](https://console.zitadel.ch), our administration UI for ZITADEL. + > All documentations are under active work and subject to change soon! ### Features diff --git a/site/docs/administrate/01-console.de.md b/site/docs/administrate/01-console.de.md deleted file mode 100644 index 54759f73ed..0000000000 --- a/site/docs/administrate/01-console.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Console ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/01-console.en.md b/site/docs/administrate/01-console.md similarity index 100% rename from site/docs/administrate/01-console.en.md rename to site/docs/administrate/01-console.md diff --git a/site/docs/administrate/02-organisations.de.md b/site/docs/administrate/02-organisations.de.md deleted file mode 100644 index 88fc1ffc1d..0000000000 --- a/site/docs/administrate/02-organisations.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Organisationen ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/02-organisations.en.md b/site/docs/administrate/02-organisations.md similarity index 100% rename from site/docs/administrate/02-organisations.en.md rename to site/docs/administrate/02-organisations.md diff --git a/site/docs/administrate/03-projects.de.md b/site/docs/administrate/03-projects.de.md deleted file mode 100644 index 9421b86c01..0000000000 --- a/site/docs/administrate/03-projects.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Projekte ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/03-projects.en.md b/site/docs/administrate/03-projects.md similarity index 100% rename from site/docs/administrate/03-projects.en.md rename to site/docs/administrate/03-projects.md diff --git a/site/docs/administrate/04-clients.de.md b/site/docs/administrate/04-clients.de.md deleted file mode 100644 index b7f53dfa17..0000000000 --- a/site/docs/administrate/04-clients.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Clients ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/04-clients.en.md b/site/docs/administrate/04-clients.md similarity index 98% rename from site/docs/administrate/04-clients.en.md rename to site/docs/administrate/04-clients.md index 4fdc436b29..1de64e4c48 100644 --- a/site/docs/administrate/04-clients.en.md +++ b/site/docs/administrate/04-clients.md @@ -13,7 +13,7 @@ Typical types of applications are: * User Agent (Single-Page-Application) * Native -Check out our [Integration Guide](integrate#Overview) for more information. +Check out our [Integration Guide](quickstarts#Overview) for more information. ### Manage clients diff --git a/site/docs/administrate/05-roles.de.md b/site/docs/administrate/05-roles.de.md deleted file mode 100644 index 26565011a6..0000000000 --- a/site/docs/administrate/05-roles.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Rollen ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/05-roles.en.md b/site/docs/administrate/05-roles.md similarity index 95% rename from site/docs/administrate/05-roles.en.md rename to site/docs/administrate/05-roles.md index 4bd418446e..caa4470d34 100644 --- a/site/docs/administrate/05-roles.en.md +++ b/site/docs/administrate/05-roles.md @@ -10,7 +10,7 @@ title: Roles For more information about how **roles** can be consumed, have a look the protocol specific information. -- [OpenID Connect / OAuth](integrate#How_to_consume_authorizations_in_your_application_or_service) +- [OpenID Connect / OAuth](quickstarts#How_to_consume_authorizations_in_your_application_or_service) ### Manage Roles diff --git a/site/docs/administrate/06-users.de.md b/site/docs/administrate/06-users.de.md deleted file mode 100644 index ab58e1c2e0..0000000000 --- a/site/docs/administrate/06-users.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Benutzer ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/06-users.en.md b/site/docs/administrate/06-users.md similarity index 100% rename from site/docs/administrate/06-users.en.md rename to site/docs/administrate/06-users.md diff --git a/site/docs/administrate/07-policies.de.md b/site/docs/administrate/07-policies.de.md deleted file mode 100644 index f78961becd..0000000000 --- a/site/docs/administrate/07-policies.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Richtlinien ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/07-policies.en.md b/site/docs/administrate/07-policies.md similarity index 100% rename from site/docs/administrate/07-policies.en.md rename to site/docs/administrate/07-policies.md diff --git a/site/docs/administrate/08-providers.de.md b/site/docs/administrate/08-providers.de.md deleted file mode 100644 index 428e170df5..0000000000 --- a/site/docs/administrate/08-providers.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Identitäts Provider ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/08-providers.en.md b/site/docs/administrate/08-providers.md similarity index 100% rename from site/docs/administrate/08-providers.en.md rename to site/docs/administrate/08-providers.md diff --git a/site/docs/administrate/09-authorizations.de.md b/site/docs/administrate/09-authorizations.de.md deleted file mode 100644 index 42e58c92a8..0000000000 --- a/site/docs/administrate/09-authorizations.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Authorizations ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/09-authorizations.en.md b/site/docs/administrate/09-authorizations.md similarity index 100% rename from site/docs/administrate/09-authorizations.en.md rename to site/docs/administrate/09-authorizations.md diff --git a/site/docs/administrate/09-management-roles.de.md b/site/docs/administrate/09-management-roles.de.md deleted file mode 100644 index 95481566c9..0000000000 --- a/site/docs/administrate/09-management-roles.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Management Rollen ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/09-management-roles.en.md b/site/docs/administrate/09-management-roles.md similarity index 100% rename from site/docs/administrate/09-management-roles.en.md rename to site/docs/administrate/09-management-roles.md diff --git a/site/docs/administrate/70-zitadelroles.de.md b/site/docs/administrate/70-zitadelroles.de.md deleted file mode 100644 index d44dec90f4..0000000000 --- a/site/docs/administrate/70-zitadelroles.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: ZITADEL Rollen ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/70-zitadelroles.en.md b/site/docs/administrate/70-zitadelroles.md similarity index 100% rename from site/docs/administrate/70-zitadelroles.en.md rename to site/docs/administrate/70-zitadelroles.md diff --git a/site/docs/administrate/80-audit.de.md b/site/docs/administrate/80-audit.de.md deleted file mode 100644 index abb8125fb8..0000000000 --- a/site/docs/administrate/80-audit.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Audit ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/80-audit.en.md b/site/docs/administrate/80-audit.md similarity index 100% rename from site/docs/administrate/80-audit.en.md rename to site/docs/administrate/80-audit.md diff --git a/site/docs/administrate/90-system.de.md b/site/docs/administrate/90-system.de.md deleted file mode 100644 index 99a0c4ac05..0000000000 --- a/site/docs/administrate/90-system.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: System Administration ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/administrate/90-system.en.md b/site/docs/administrate/90-system.md similarity index 98% rename from site/docs/administrate/90-system.en.md rename to site/docs/administrate/90-system.md index 96b6909ec4..4e287ac3e4 100644 --- a/site/docs/administrate/90-system.en.md +++ b/site/docs/administrate/90-system.md @@ -38,7 +38,7 @@ There is even a possibility to regenerate a read model. -> Additional infos to the architecture of ZITADEL is located in [Architecture Docs](documentation#Architecture) +> Additional infos to the architecture of ZITADEL is located in [Architecture Docs](architecture#Architecture) ### Secret Handling diff --git a/site/docs/administrate/seo_de.html b/site/docs/administrate/seo_de.html deleted file mode 100644 index 19e0f914f2..0000000000 --- a/site/docs/administrate/seo_de.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/site/docs/administrate/seo_en.html b/site/docs/administrate/seo_en.html deleted file mode 100644 index ec0b59a18b..0000000000 --- a/site/docs/administrate/seo_en.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/site/docs/angular/00-overview.md b/site/docs/angular/00-overview.md new file mode 100644 index 0000000000..19a97dd7cc --- /dev/null +++ b/site/docs/angular/00-overview.md @@ -0,0 +1,16 @@ +--- +title: Angular +--- + +
+ zitadel + + angular +
+ +This Integration guide shows you the recommended way to integrate **ZITADEL** into your Angular Application. +It demonstrates how to add a user login to your application and fetch some data from the user info endpoint. + +At the end of the guide you should have an application able to login a user and read the user profile. + +> Note that our **ZITADEL Console** is also written in Angular and can therefore be used as a reference. \ No newline at end of file diff --git a/site/docs/angular/01-configure.md b/site/docs/angular/01-configure.md new file mode 100644 index 0000000000..690bafa6b9 --- /dev/null +++ b/site/docs/angular/01-configure.md @@ -0,0 +1,29 @@ +--- +title: Configure Zitadel +--- + +### Setup Application and get Keys + +Before we can start building our application we have do do a few configuration steps in ZITADEL Console. +You will need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your [Project](https://console.zitadel.ch/projects) and add a new application at the top of the page. +Select Web Application and continue. +We recommend that you use [Authorization Code](architecture#Authorization_Code) in combination with [Proof Key for Code Exchange](architecture#Proof_Key_for_Code_Exchange) for all web applications. + +> Make sure Authentication Method is set to `NONE` and the Application Type is set to `SPA` or `NATIVE`. + +#### Redirect URLs + +A redirect URL is a URL in your application where ZITADEL redirects the user after they have authenticated. Set your url to the domain the web app will be deployed to or use `localhost:4200` for development as Angular will be running on port 4200. + +> If you are following along with the sample project you downloaded from our templates, you should set the Allowed Callback URL to http://localhost:4200/auth/callback. You will also have to set dev mode to `true` as this will enable unsecure http for the moment. + +If you want to redirect the users back to a route on your application after they have logged out, add an optional redirect in the post redirectURI field. + +Continue and Create the application. + +#### Client ID and Secret + +After successful app creation a popup will appear showing you your clientID as well as a secret. +Copy your client ID as it will be needed in the next step. + +> Note: You will be able to regenerate the secret at a later time, though it is not needed for SPAs with Authorization Code Flow. diff --git a/site/docs/angular/02-code.md b/site/docs/angular/02-code.md new file mode 100644 index 0000000000..b2596a67d4 --- /dev/null +++ b/site/docs/angular/02-code.md @@ -0,0 +1,198 @@ +--- +title: Angular Setup +--- + +### Install Angular dependencies + +You need to install an oauth / oidc client to connect with ZITADEL. Run the following command: + +```bash +npm install angular-oauth2-oidc +``` + +This library helps integrating ZITADEL Authentication in your Angular Application. + +### Create and configure Auth Module + +Add the Auth module to your Angular imports in AppModule and setup the AuthConfig in a constant above. + +```ts +... +import { AuthConfig, OAuthModule } from 'angular-oauth2-oidc'; + +const authConfig: AuthConfig = { + clientId: 'YOUR CLIENT ID', + redirectUri: 'http://localhost:4200/auth/callback', // change this to your domain later or use window.location.origin. + scope: 'openid profile email', + responseType: 'code', + oidc: true, +}; + +@NgModule({ + declarations: [ + AppComponent, + SignedoutComponent, + ], + imports: [ + OAuthModule..forRoot(), +... +``` + +Set **openid**, **profile** and **email** as scope, **code** as responseType, and oidc to **true**. +Then create a Authentication Service to provide the functions to authenticate your user. + +You can use Angulars schematics to do so: + +``` bash +ng g component services/auth +``` +This will create an AuthService automatically for you. + +Copy the following code to your service. This code provides a function `authenticate()` which redirects the user to ZITADEL. After the user has logged in it will be redirected back to your redirectURI set in Auth Module and Console. Make sure both correspond, otherwise ZITADEL will throw an error. + +```ts +import { AuthConfig, OAuthService } from 'angular-oauth2-oidc'; + +export default class AuthService { + private authConfig!: AuthConfig; + private _authenticated: boolean = false; + private readonly _authenticationChanged: BehaviorSubject< + boolean + > = new BehaviorSubject(this.authenticated); + + constructor( + private oauthService: OAuthService, + private statehandler: StatehandlerService, + ) { } + + public get authenticated(): boolean { + return this._authenticated; + } + + public get authenticationChanged(): Observable { + return this._authenticationChanged; + } + + public getOIDCUser(): Observable { + return from(this.oauthService.loadUserProfile()); + } + + public async authenticate(): Promise { + this.oauthService.configure(this.authConfig); + + this.oauthService.strictDiscoveryDocumentValidation = false; + await this.oauthService.loadDiscoveryDocumentAndTryLogin(); + + this._authenticated = this.oauthService.hasValidAccessToken(); + + if (!this.oauthService.hasValidIdToken() || !this.authenticated || partialConfig || force) { + const newState = setState ? await this.statehandler.createState().toPromise() : undefined; + this.oauthService.initCodeFlow(newState); + } + this._authenticationChanged.next(this.authenticated); + + return this.authenticated; + } + + public signout(): void { + this.oauthService.logOut(); + this._authenticated = false; + this._authenticationChanged.next(false); + } +} +``` + +### Add Login in your application + +To login a user, a component or a guard is needed. + +- A component provides a button prompting the user to start the login flow. +`authenticate()` redirects your user to ZITADEL.ch for authentication. Upon successfull Authentication, ZITADEL will redirect the user back to your previously defined Redirect URL. + +- A guard can be setup to check if the user has a valid **Access Token** to proceed. This will check if the user has a stored **accesstoken** in storage or otherwise prompt the user to login. + +The use of this components totally depends on your application. In most cases you need both. + +To create a component use +``` bash +ng g component components/login +``` +and then inject the authService to call `authenticate()`. + +Same for the guard: +``` bash +ng g guard guards/auth +``` + +This code shows the AuthGuard used in our Console. + +```ts +import { AuthService } from 'src/app/services/auth.service'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthGuard implements CanActivate { + constructor(private auth: AuthService) { } + + public canActivate( + _: ActivatedRouteSnapshot, + state: RouterStateSnapshot, + ): Observable | Promise | boolean { + if (!this.auth.authenticated) { + return this.auth.authenticate(); + } + return this.auth.authenticated; + } +} +``` + +it can easily be added to your RouterModule. + +```ts +... +const routes: Routes = [ + { + path: '', + loadChildren: () => import('./pages/home/home.module').then(m => m.HomeModule), + canActivate: [AuthGuard], + }, +... +``` + +### Add Logout in your application + +The authService and Library also provides a useful function for logging out your users. Just call `auth.signout()` to log out your user. Note that you can also configure your Logout Redirect URL if you want your Users to be redirected after logout. + +```ts +import { AuthService } from 'src/app/services/auth.service.ts'; + +export class SomeComponentWithLogout { + constructor(private authService: AuthService){} + + public signout(): Promise { + return this.authService.signout(); + } +} +``` + +### Show User Information + +To fetch user data, ZITADELS user info endpoint has to be called. This data contains sensitive information and artifacts related to your users identity and the scopes you defined in your Auth Config. +Our AuthService already includes a function called getOIDCUser(). You can call it whereever you need this information. + +```ts +import { AuthService } from 'src/app/services/auth.service.ts'; + +export class SomeComponentWithUserInfo { + constructor(public authService: AuthService){} +} +``` + +and in your html + +```html +
+

{{oidcInfo | json}}

+
+``` \ No newline at end of file diff --git a/site/docs/angular/03-end.md b/site/docs/angular/03-end.md new file mode 100644 index 0000000000..813951affe --- /dev/null +++ b/site/docs/angular/03-end.md @@ -0,0 +1,9 @@ +--- +title: Completion +--- + +### What next? + +You have successfully integrated ZITADEL in your Angular Application! Now you can proceed implementing our APIs to include Authorization. Refer to our [Docs](apis#Authentication_API) or checkout our Console Code on [Github](https://github.com/caos/zitadel) which is using GRPC to access data. + +For more information about creating an angular application we refer to [Angular](https://angular.io/start) and for more information about the used oauth/oidc library consider reading their docs at [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc). \ No newline at end of file diff --git a/site/docs/develop/00-overview.en.md b/site/docs/apis/00-overview.md similarity index 95% rename from site/docs/develop/00-overview.en.md rename to site/docs/apis/00-overview.md index ec508cc0a8..8af2ffed13 100644 --- a/site/docs/develop/00-overview.en.md +++ b/site/docs/apis/00-overview.md @@ -3,6 +3,8 @@ title: Overview description: … --- + + > All documentations are under active work and subject to change soon! ### APIs diff --git a/site/docs/develop/01-authentication.en.md b/site/docs/apis/01-authentication.md similarity index 100% rename from site/docs/develop/01-authentication.en.md rename to site/docs/apis/01-authentication.md diff --git a/site/docs/develop/02-management.en.md b/site/docs/apis/02-management.md similarity index 100% rename from site/docs/develop/02-management.en.md rename to site/docs/apis/02-management.md diff --git a/site/docs/develop/03-administration.en.md b/site/docs/apis/03-administration.md similarity index 100% rename from site/docs/develop/03-administration.en.md rename to site/docs/apis/03-administration.md diff --git a/site/docs/documentation/00-overview.en.md b/site/docs/architecture/00-overview.md similarity index 83% rename from site/docs/documentation/00-overview.en.md rename to site/docs/architecture/00-overview.md index aab91c5dff..577cde7217 100644 --- a/site/docs/documentation/00-overview.en.md +++ b/site/docs/architecture/00-overview.md @@ -2,6 +2,8 @@ title: Overview --- + + > All documentations are under active work and subject to change soon! This part of the **ZITADEL** documentation comprises three major subject areas: diff --git a/site/docs/documentation/01-priciples.en.md b/site/docs/architecture/01-priciples.md similarity index 100% rename from site/docs/documentation/01-priciples.en.md rename to site/docs/architecture/01-priciples.md diff --git a/site/docs/documentation/02-architecture.en.md b/site/docs/architecture/02-architecture.md similarity index 99% rename from site/docs/documentation/02-architecture.en.md rename to site/docs/architecture/02-architecture.md index 9d7b986675..6921af9e96 100644 --- a/site/docs/documentation/02-architecture.en.md +++ b/site/docs/architecture/02-architecture.md @@ -97,3 +97,4 @@ With this design even the outage of a whole data-center would have a minimal imp
Multi-Cluster Architecture
+ diff --git a/site/docs/documentation/03-openidoauth.en.md b/site/docs/architecture/03-openidoauth.md similarity index 97% rename from site/docs/documentation/03-openidoauth.en.md rename to site/docs/architecture/03-openidoauth.md index 6f961bd1b8..3e22faaec0 100644 --- a/site/docs/documentation/03-openidoauth.en.md +++ b/site/docs/architecture/03-openidoauth.md @@ -3,22 +3,21 @@ title: OpenID Connect 1.0 & OAuth 2.0 --- ### Endpoints and Domains - This chapter documents the [OpenID Connect 1.0](https://openid.net/connect/) and [OAuth 2.0](https://oauth.net/2/) features provided by **ZITADEL**. Under normal circumstances **ZITADEL** need four domain names to operate properly. | Domain Name | Example | Description | |:------------|:----------------------|--------------------------------------------------------------------------------------------------------------------------------------| -| issuer | `issuer.zitadel.ch` | Provides the [OpenID Connect 1.0 Discovery Endpoint](#openid-connect-10-discovery) | -| api | `api.zitadel.ch` | All ZITADEL API's are located under this domain see [API explanation](develop#APIs) for details | +| issuer | `issuer.zitadel.ch` | Provides the [OpenID Connect 1.0 Discovery Endpoint](#OpenID_Connect_1_0_Discovery) | +| api | `api.zitadel.ch` | All ZITADEL API's are located under this domain see [API explanation](apis#APIs) for details | | login | `accounts.zitadel.ch` | The accounts.* page provides server renderer pages like login and register and as well the authorization_endpoint for OpenID Connect | | console | `console.zitadel.ch` | With the console.* domain we serve the assets for the management gui | #### OpenID Connect 1.0 Discovery The OpenID Connect Discovery Endpoint is located within the issuer domain. -For example with [zitadel.ch](zitadel.ch) this would be the domain [issuer.zitadel.ch](issuer.zitadel.ch). This would give us [https://issuer.zitadel.ch/.well-known/openid-configuration](https://issuer.zitadel.ch/.well-known/openid-configuration). +For example with [zitadel.ch](https://zitadel.ch), issuer.zitadel.ch would be the domain. This would give us [https://issuer.zitadel.ch/.well-known/openid-configuration](https://issuer.zitadel.ch/.well-known/openid-configuration). **Link to spec.** [OpenID Connect Discovery 1.0 incorporating errata set 1](https://openid.net/specs/openid-connect-discovery-1_0.html) @@ -50,7 +49,7 @@ For example with [zitadel.ch](zitadel.ch) this would be the domain [issuer.zitad #### OAuth 2.0 Metadata -**ZITADEL** does not yet provide a OAuth 2.0 Metadata endpoint but instead provides a [OpenID Connect Discovery Endpoint](#openid-connect-10-discovery). +**ZITADEL** does not yet provide a OAuth 2.0 Metadata endpoint but instead provides a [OpenID Connect Discovery Endpoint](#OpenID_Connect_1_0_Discovery). ### Scopes @@ -287,3 +286,4 @@ curl --request POST \ #### Security Assertion Markup Language (SAML) 2.0 Profile **Link to spec.** [Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://tools.ietf.org/html/rfc7522) + diff --git a/site/docs/aspnet/00-overview.md b/site/docs/aspnet/00-overview.md new file mode 100644 index 0000000000..1f8f346f8c --- /dev/null +++ b/site/docs/aspnet/00-overview.md @@ -0,0 +1,15 @@ +--- +title: ASP.NET Auth and Authz +description: ... +--- + +
+ zitadel + + aspnet +
+ + +> This quickstart is not yet released. Come back later! + +This Integration guide shows you the recommended way on how to integrate **ZITADEL** into your ASP NET Backend. diff --git a/site/docs/develop/00-overview.de.md b/site/docs/develop/00-overview.de.md deleted file mode 100644 index ee626691f2..0000000000 --- a/site/docs/develop/00-overview.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Übersicht -description: … ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/develop/01-authentication.de.md b/site/docs/develop/01-authentication.de.md deleted file mode 100644 index ee94e19c7b..0000000000 --- a/site/docs/develop/01-authentication.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Authentication API -description: … ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/develop/02-management.de.md b/site/docs/develop/02-management.de.md deleted file mode 100644 index 18adf5bf64..0000000000 --- a/site/docs/develop/02-management.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Management API -description: … ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/develop/03-administration.de.md b/site/docs/develop/03-administration.de.md deleted file mode 100644 index b6b59725b6..0000000000 --- a/site/docs/develop/03-administration.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Admin API -description: … ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/documentation/00-overview.de.md b/site/docs/documentation/00-overview.de.md deleted file mode 100644 index 6d9be1de6e..0000000000 --- a/site/docs/documentation/00-overview.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Übersicht ---- - -> This Language is not yet translated. Please consult the English version. \ No newline at end of file diff --git a/site/docs/documentation/01-priciples.de.md b/site/docs/documentation/01-priciples.de.md deleted file mode 100644 index 51456b6b62..0000000000 --- a/site/docs/documentation/01-priciples.de.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: Prinzipien ---- - -- Seien Sie bei Ihren Entscheidungen transparent -- Zustandsloses Anwendungsdesign -- Das System der Aufzeichnungen ist der Ereignisspeicher -- Alles andere muss regeneriert werden können -- Versuchen Sie nicht, komplexe Probleme außerhalb des IAM-Bereichs zu lösen -- Verwenden Sie skalierbaren Speicher für den Ereignisspeicher und die Abfragemodelle -- Versuchen Sie, wenn immer möglich idempotent zu sein -- Reduzieren Sie die Notwendigkeit von System- oder externen Abhängigkeiten so weit wie möglich -- Automatisierung einbeziehen -- Zuerst die Design-API -- Optimieren Sie alle Komponenten für den Betrieb am zweiten Tag -- Verwenden Sie nur Open-Source-Projekte mit permissiven Lizenzen -- Rollen Sie nicht Ihre eigene Krypto -- Standard so weit wie möglich einbeziehen -- Nutzen Sie die Funktionen der Plattform -- Mit einem CDN und einer WAF laufen können diff --git a/site/docs/documentation/02-architecture.de.md b/site/docs/documentation/02-architecture.de.md deleted file mode 100644 index 3f807cab7e..0000000000 --- a/site/docs/documentation/02-architecture.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Architektur ---- - -> TBD diff --git a/site/docs/documentation/03-openidoauth.de.md b/site/docs/documentation/03-openidoauth.de.md deleted file mode 100644 index 98d47d61d8..0000000000 --- a/site/docs/documentation/03-openidoauth.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: OpenID Connect 1.0 & OAuth 2.0 ---- - -> TBD diff --git a/site/docs/export-log b/site/docs/export-log new file mode 100644 index 0000000000..8680b49e13 --- /dev/null +++ b/site/docs/export-log @@ -0,0 +1,78 @@ + 4.66 kB service-worker-index.html + 10.5 kB index.html + 1.15 kB icons/favicon.ico + 481 B manifest.json + 3.17 kB prism.css + 11.6 kB default-skin/default-skin.css + 14.8 kB client/client-48a0d285.css + 3.88 kB client/index-5d76bab7.css + 4.14 kB photoswipe.css + 33.9 kB client/client.ea30bda8.js + 559 B client/inject_styles.5607aec6.js + 18.1 kB client/index.6f1d050b.js + 5.54 kB start.json + 967 B quickstarts.json + 6.45 kB logos/zitadel-logo-light.svg + 8.3 kB logos/zitadel-logo-solo-darkdesign.svg + 18 kB start/index.html + 14.9 kB img/develop2.png + 10.1 kB img/projects2.png + 11.1 kB img/personal2.png + 7.96 kB quickstarts/index.html + 15.2 kB use/index.html + 22.8 kB apis/index.html + 73.1 kB img/accounts_org_register.png + 10.3 kB client/[slug]-3cb415b8.css + 1.52 kB client/index-4d174bf5.css + 20.7 kB client/[slug].aebb20dc.js + 4.16 kB client/index.c2043c63.js + 114 kB architecture/index.html + 207 kB img/accounts_password.png + 199 kB img/accounts_page.png + 899 B tech/angular.svg + 1.79 kB tech/dart.svg + 2.29 kB tech/golang.svg + 200 kB img/accounts_otp_verify.png + 21.3 kB img/zitadel_software_architecture.png + 10.4 kB img/zitadel_cluster_architecture.png + 184 kB administrate/index.html + 39 kB angular/index.html + 38.9 kB dart/index.html + 38.8 kB go/index.html + 7.37 kB img/zitadel_multicluster_architecture.png + 31 kB img/console_user_entry.png + 83.5 kB img/console_admin_entry.png + 167 kB img/console_admin_system.png + 129 kB img/console_org_domain_default.png + 126 kB img/console_org_domain_add.png + 142 kB img/console_org_domain_added.png + 126 kB img/console_user_personal_information.png + 135 kB img/console_org_domain_verify.png + 52.8 kB img/console_user_personal_info.png + 15 kB img/console_projects_empty.png + 40.2 kB img/console_projects_my_first_project.png + 40.7 kB img/console_org_domain_verified.png + 178 kB img/console_org_manage_roles_2.png + 129 kB img/console_org_manage_roles_1.png + 38.1 kB img/console_org_domain_verify_dns.png + 112 kB img/console_project_manage_roles_1.png + 19.5 kB img/console_clients_my_first_spa_wizard_1.png + 21.6 kB img/console_clients_my_first_spa_wizard_4.png + 22.3 kB img/console_clients_my_first_spa_wizard_3.png + 28.3 kB img/console_clients_my_first_spa_config.png + 27.1 kB img/console_user_list_search.png + 159 kB img/console_project_manage_roles_2.png + 25.2 kB img/console_user_list.png + 16.3 kB img/console_clients_my_first_spa_wizard_2.png + 38.8 kB img/console_authz_add_1.png + 77.2 kB administrate.json + 26.6 kB img/console_authz_overview.png + 186 kB img/console_user_manage_roles_1.png + 47.9 kB img/console_user_manage_roles_2.png + 110 kB img/console_user_create_done.png + 36.1 kB img/console_authz_add_2.png + 34.1 kB img/console_authz_add_3.png + 29.6 kB img/console_user_create_form.png + 6.53 kB apis.json + 142 kB img/console_iam_admin_failed.png + 167 kB img/console_iam_admin_views.png \ No newline at end of file diff --git a/site/docs/flutter/00-overview.md b/site/docs/flutter/00-overview.md new file mode 100644 index 0000000000..d4e370f3bf --- /dev/null +++ b/site/docs/flutter/00-overview.md @@ -0,0 +1,14 @@ +--- +title: Flutter Login +description: ... +--- + +
+ zitadel + + flutter +
+ +> This quickstart is not yet released. Come back later! + +This Integration guide shows you the recommended way on how to integrate **ZITADEL** into your Dart Application. diff --git a/site/docs/go/00-overview.md b/site/docs/go/00-overview.md new file mode 100644 index 0000000000..af5fafb423 --- /dev/null +++ b/site/docs/go/00-overview.md @@ -0,0 +1,15 @@ +--- +title: Dart Login +description: ... +--- + +
+ zitadel + + golang +
+ + +> This quickstart is not yet released. Come back later! + +This Integration guide shows you the recommended way on how to integrate **ZITADEL** into your Go Lang Backend. diff --git a/site/docs/integrate/00-overview.de.md b/site/docs/integrate/00-overview.de.md deleted file mode 100644 index 5057e686af..0000000000 --- a/site/docs/integrate/00-overview.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Übersicht -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/integrate/01-singlepageapp.de.md b/site/docs/integrate/01-singlepageapp.de.md deleted file mode 100644 index 5766daed2d..0000000000 --- a/site/docs/integrate/01-singlepageapp.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Single Page Application -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/integrate/02-serverapp.de.md b/site/docs/integrate/02-serverapp.de.md deleted file mode 100644 index 7fa8515343..0000000000 --- a/site/docs/integrate/02-serverapp.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Server Side Application -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/integrate/03-mobileapp.de.md b/site/docs/integrate/03-mobileapp.de.md deleted file mode 100644 index 085e060c06..0000000000 --- a/site/docs/integrate/03-mobileapp.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Mobile Application -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/integrate/04-nativeapp.de.md b/site/docs/integrate/04-nativeapp.de.md deleted file mode 100644 index a96998467d..0000000000 --- a/site/docs/integrate/04-nativeapp.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Native Application -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/integrate/05-proxy.de.md b/site/docs/integrate/05-proxy.de.md deleted file mode 100644 index 5a605d7965..0000000000 --- a/site/docs/integrate/05-proxy.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Proxy / WAF -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/integrate/06-api.de.md b/site/docs/integrate/06-api.de.md deleted file mode 100644 index a4961d6a29..0000000000 --- a/site/docs/integrate/06-api.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: API -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/integrate/07-products.de.md b/site/docs/integrate/07-products.de.md deleted file mode 100644 index 9eaf2a9817..0000000000 --- a/site/docs/integrate/07-products.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Products -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/oauth2-proxy/00-overview.md b/site/docs/oauth2-proxy/00-overview.md new file mode 100644 index 0000000000..165e1bbe86 --- /dev/null +++ b/site/docs/oauth2-proxy/00-overview.md @@ -0,0 +1,13 @@ +--- +title: OAuth 2.0 Proxy +--- + +
+ zitadel + + oauth2-proxy +
+ +### OAuth2 Proxy Project + +[OAuth2-proxy](https://github.com/oauth2-proxy/oauth2-proxy) is a project which allows services to delegate the authentication flow to a IDP, for example **ZITADEL** \ No newline at end of file diff --git a/site/docs/oauth2-proxy/01-configure-zitadel.md b/site/docs/oauth2-proxy/01-configure-zitadel.md new file mode 100644 index 0000000000..5a763fdb71 --- /dev/null +++ b/site/docs/oauth2-proxy/01-configure-zitadel.md @@ -0,0 +1,29 @@ +--- +title: Configure Zitadel +--- + +### Setup Application and get Keys + +Before we can start building our application we have do do a few configuration steps in ZITADEL Console. +You will need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your [Project](https://console.zitadel.ch/projects) and add a new application at the top of the page. +Select Web Application and continue. +We recommend that you use [Authorization Code](architecture#Authorization_Code) for the OAuth 2.0 Proxy. + +> Make sure Authentication Method is set to `BASIC` and the Application Type is set to `Web`. + +#### Redirect URLs + +A redirect URL is a URL in your application where ZITADEL redirects the user after they have authenticated. Set your url to the domain the proxy will be deployed to or use the default one `http://127.0.0.1:4180/oauth2/callback`. + +> If you are following along with the sample project you downloaded from our templates, you should set the Allowed Callback URL to http://localhost:4200/auth/callback. You will also have to set dev mode to `true` as this will enable unsecure http for the moment. + +If you want to redirect the users back to a route on your application after they have logged out, add an optional redirect in the post redirectURI field. + +Continue and Create the application. + +#### Client ID and Secret + +After successful app creation a popup will appear showing you your clientID as well as a secret. +Copy your client ID and Secrets as it will be needed in the next step. + +> Note: You will be able to regenerate the secret at a later time if you loose it. diff --git a/site/docs/oauth2-proxy/02-configure-proxy.md b/site/docs/oauth2-proxy/02-configure-proxy.md new file mode 100644 index 0000000000..7d515b76a1 --- /dev/null +++ b/site/docs/oauth2-proxy/02-configure-proxy.md @@ -0,0 +1,28 @@ +--- +title: OAuth 2.0 Proxy Setup +--- + +### Authentication Example + +```toml +provider = "oidc" +user_id_claim = "sub" #uses the subject as ID instead of the email +provider_display_name = "ZITADEL" +redirect_url = "http://127.0.0.1:4180/oauth2/callback" +oidc_issuer_url = "https://issuer.zitadel.ch" +upstreams = [ + "https://example.corp.com" +] +email_domains = [ + "*" +] +client_id = "{ZITADEL_GENERATED_CLIENT_ID}" +client_secret = "{ZITADEL_GENERATED_CLIENT_SECRET}" +pass_access_token = true +cookie_secret = "{SUPPLY_SOME_SECRET_HERE}" +skip_provider_button = true +cookie_secure = false #localdev only false +http_address = "127.0.0.1:4180" #localdev only +``` + +> This was tested with version `oauth2-proxy v6.1.1 (built with go1.14.2)` \ No newline at end of file diff --git a/site/docs/oauth2-proxy/03-end.md b/site/docs/oauth2-proxy/03-end.md new file mode 100644 index 0000000000..ede964b4d7 --- /dev/null +++ b/site/docs/oauth2-proxy/03-end.md @@ -0,0 +1,7 @@ +--- +title: Completion +--- + +### What next? + +You have successfully integrated ZITADEL in your proxy! \ No newline at end of file diff --git a/site/docs/integrate/00-overview.en.md b/site/docs/quickstarts/00-overview.md similarity index 67% rename from site/docs/integrate/00-overview.en.md rename to site/docs/quickstarts/00-overview.md index 6dcc0a212f..b549f3d610 100644 --- a/site/docs/integrate/00-overview.en.md +++ b/site/docs/quickstarts/00-overview.md @@ -13,4 +13,4 @@ This Integration guide gives you recommendations on how to integrate different C - Native App - Reverse Proxy -For more details about how **ZITADEL** treats [scopes](documentation#Scopes) and [claims](documentation#Claims) see the [documentations](documentation). +For more details about how **ZITADEL** treats [scopes](architecture#Scopes) and [claims](architecture#Claims) see the [documentations](architecture). diff --git a/site/docs/integrate/01-singlepageapp.en.md b/site/docs/quickstarts/01-singlepageapp.md similarity index 90% rename from site/docs/integrate/01-singlepageapp.en.md rename to site/docs/quickstarts/01-singlepageapp.md index 4617f720a6..063f8a8d69 100644 --- a/site/docs/integrate/01-singlepageapp.en.md +++ b/site/docs/quickstarts/01-singlepageapp.md @@ -5,7 +5,7 @@ description: ... ### SPA Protocol and Flow recommendation -If your [client](administrate#Clients) is a single page application (SPA) we recommend that you use [Authorization Code](documentation#Authorization_Code) in combination with [Proof Key for Code Exchange](documentation#Proof_Key_for_Code_Exchange). +If your [client](administrate#Clients) is a single page application (SPA) we recommend that you use [Authorization Code](architecture#Authorization_Code) in combination with [Proof Key for Code Exchange](architecture#Proof_Key_for_Code_Exchange). This flow has great support with most modern languages and frameworks and is the recommended default. diff --git a/site/docs/integrate/02-serverapp.en.md b/site/docs/quickstarts/02-serverapp.md similarity index 100% rename from site/docs/integrate/02-serverapp.en.md rename to site/docs/quickstarts/02-serverapp.md diff --git a/site/docs/integrate/03-mobileapp.en.md b/site/docs/quickstarts/03-mobileapp.md similarity index 100% rename from site/docs/integrate/03-mobileapp.en.md rename to site/docs/quickstarts/03-mobileapp.md diff --git a/site/docs/integrate/04-nativeapp.en.md b/site/docs/quickstarts/04-nativeapp.md similarity index 100% rename from site/docs/integrate/04-nativeapp.en.md rename to site/docs/quickstarts/04-nativeapp.md diff --git a/site/docs/integrate/05-proxy.en.md b/site/docs/quickstarts/05-proxy.md similarity index 100% rename from site/docs/integrate/05-proxy.en.md rename to site/docs/quickstarts/05-proxy.md diff --git a/site/docs/integrate/06-api.en.md b/site/docs/quickstarts/06-api.md similarity index 100% rename from site/docs/integrate/06-api.en.md rename to site/docs/quickstarts/06-api.md diff --git a/site/docs/integrate/07-products.en.md b/site/docs/quickstarts/07-products.md similarity index 100% rename from site/docs/integrate/07-products.en.md rename to site/docs/quickstarts/07-products.md diff --git a/site/docs/start/00-quick-start.de.md b/site/docs/start/00-quick-start.de.md deleted file mode 100644 index 8f73dcc2f8..0000000000 --- a/site/docs/start/00-quick-start.de.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Schnellstart -description: ... ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/start/00-quick-start.en.md b/site/docs/start/00-quick-start.en.md deleted file mode 100644 index 0dc659a15d..0000000000 --- a/site/docs/start/00-quick-start.en.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Quick-Start -description: A quick-start reference for the impatient reader. ---- - -> All documentations are under active work and subject to change soon! - -### Trying out ZITADEL - -You can either use our cloud-instance [ZITADEL.ch](https://zitadel.ch) or deploy a dedicated **ZITADEL** instance. To get started, we recommend you try out our free tier on [ZITADEL.ch](https://zitadel.ch). - -### Use ZITADEL.ch - -To use ZITADEL in your project you need to: -1. Set up a new [client](administrate#What_are_clients) and [users](administrate#What_are_users) in our [Console](administrate#What_is_Console); -2. Configure your project to use ZITADEL. - -To register your free [organisation](administrate#Organisations), visit this link [register organisation](https://accounts.zitadel.ch/register/org). -After accepting the TOS and filling out all the required fields you will receive an email with further instructions. - - - -#### Verify your domain name (optional) - -When you verify your domain you get the benefit that your [organisations](administrate#Organisations) [users](administrate#Users) can use this domain as **preferred logonname**. You find a more detailed explanation [How ZITADEL handles usernames](administrate#How_ZITADEL_handles_usernames). - -The verification process is documented [here](administrate#Verify_a_domain_name) - -#### Add Users to your organisation - -To add new user just follow [this guide](administrate#Create_Users) - -#### Setup an application - -First [create a project](administrate#Create_a_project) - -Then create within this [project](administrate#Projects) a [new client](administrate#Create_a_client) - -The wizard should provide some guidance what client is the proper for you. If you are still unsure consult our [Integration Guide](integrate#Overview) - -### Use ORBOS to install ZITADEL - -> This will be added later on - -### Install ZITADEL with static manifest - -> This will be added later on diff --git a/site/docs/start/00-quick-start.md b/site/docs/start/00-quick-start.md new file mode 100644 index 0000000000..9c746c50b0 --- /dev/null +++ b/site/docs/start/00-quick-start.md @@ -0,0 +1,19 @@ +--- +title: Welcome to ZITADEL +description: A quick-start reference for the impatient reader. +--- + +Most applications need to know the identity of a user allowing to securely store user data in the cloud and provide the same personalised experience across all of the user's devices. + +ZITADEL's authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users in your application. It supports authentication using passwords and applies additional security with the help of a second factor, for example OTP, to ensure a safe and secure access. +It additionally leverages industry standards like OAuth 2.0 and OpenID Connect such that it can be easily integrated in your custom backend. + +This documentation demonstrates the different installation methods of ZITADEL and provides a quick start guide on how to register your organization as well as creating your first project. + +> All documentations are under active work and subject to change soon! + +## Trying out ZITADEL + +### Installation Types + +You can either use our cloud-instance [zitadel.ch](https://zitadel.ch) or deploy a dedicated **ZITADEL** instance. To get started, we recommend you to try out our free tier first. diff --git a/site/docs/start/01-zitadel-ch.md b/site/docs/start/01-zitadel-ch.md new file mode 100644 index 0000000000..77e310a694 --- /dev/null +++ b/site/docs/start/01-zitadel-ch.md @@ -0,0 +1,39 @@ +--- +title: ZITADEL.ch +description: A quick-start reference to use our cloud instance zitadel.ch +--- + +### Use cloud instance zitadel.ch + +To create a ZITADEL project, you have to register as an organization first. Click [here](https://accounts.zitadel.ch/register/org) to register. +You will receive an email prompting you to verify your mail. +Then go to your [Console Projects](https://console.zitadel.ch/projects) view and create a new project. + + + +Now you can proceed adding [users](administrate#What_are_users) to your organization as well as integrating your applications. We refer to our administration [documentation](administrate#What_are_clients) as well as our [quickstarts](quickstarts) to do so. + +#### Verify your domain name (optional) + +If you verify a domain you get the benefit that your organisations users can use this domain as the **preferred logonname**. You find a more detailed explanation on [how ZITADEL handles usernames](administrate#How_ZITADEL_handles_usernames) here. +The verification process is documented [here](administrate#Verify_a_domain_name) + +#### Elect Managers + +ZITADEL allows you to give other users control over ZITADEL Console itself. This can be restricted to some kind of write and/or read. This can be especially useful for directing administration over several users. You can have managers able to edit project settings and others able to create/add users only. +Read the [administration](administrate#ZITADEL_Roles) guide for more information. + +> Note: ZITADEL Managers are always located on the right sidepanel of console. + +#### Integrating an application + +After creating your project you can start integrating your applications. +After choosing your [project](https://console.zitadel.ch/projects) add a client application on the top of the page. +The wizard should provide some guidance what client is the proper for you. If you are still unsure consult our [Quickstart Guides](quickstarts). diff --git a/site/docs/start/02-orbos.md b/site/docs/start/02-orbos.md new file mode 100644 index 0000000000..e7f23c5292 --- /dev/null +++ b/site/docs/start/02-orbos.md @@ -0,0 +1,7 @@ +--- +title: ORBOS +--- + +### Use ORBOS to install ZITADEL + +> This will be added later on \ No newline at end of file diff --git a/site/docs/start/03-static-manifest.md b/site/docs/start/03-static-manifest.md new file mode 100644 index 0000000000..feefa24eb4 --- /dev/null +++ b/site/docs/start/03-static-manifest.md @@ -0,0 +1,7 @@ +--- +title: Static Manifest +--- + +### Install ZITADEL with static manifest + +> This will be added later on diff --git a/site/docs/use/00-user.de.md b/site/docs/use/00-user.de.md deleted file mode 100644 index 2fee90e9dd..0000000000 --- a/site/docs/use/00-user.de.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Benutzerhandbuch ---- - -> This Language is not yet translated. Please consult the English version. diff --git a/site/docs/use/00-user.en.md b/site/docs/use/00-user.md similarity index 94% rename from site/docs/use/00-user.en.md rename to site/docs/use/00-user.md index 3e96eae4f4..e429fe62d3 100644 --- a/site/docs/use/00-user.en.md +++ b/site/docs/use/00-user.md @@ -2,6 +2,8 @@ title: User Manual --- + + > All documentations are under active work and subject to change soon! ### Self Register User diff --git a/site/docs/vue/00-overview.md b/site/docs/vue/00-overview.md new file mode 100644 index 0000000000..ee5ab402cc --- /dev/null +++ b/site/docs/vue/00-overview.md @@ -0,0 +1,15 @@ +--- +title: Vue Login and User Endpoint +description: ... +--- + +
+ zitadel + + vue +
+ + +> This quickstart is not yet released. Come back later! + +This Integration guide shows you the recommended way on how to integrate **ZITADEL** into your Vue Frontend Application. diff --git a/site/package-lock.json b/site/package-lock.json index b30c52905d..175d547d4e 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -1187,14 +1187,6 @@ "to-fast-properties": "^2.0.0" } }, - "@formatjs/ecma402-abstract": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz", - "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==", - "requires": { - "tslib": "^2.0.1" - } - }, "@polka/send": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@polka/send/-/send-0.4.0.tgz", @@ -1206,9 +1198,9 @@ "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==" }, "@rollup/plugin-babel": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.2.tgz", - "integrity": "sha512-MjmH7GvFT4TW8xFdIeFS3wqIX646y5tACdxkTO+khbHvS3ZcVJL6vkAHLw2wqPmkhwCfWHoNsp15VYNwW6JEJA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.3.tgz", + "integrity": "sha512-DOMc7nx6y5xFi86AotrFssQqCen6CxYn+zts5KSI879d4n1hggSb4TH3mjVgG17Vc3lZziWWfcXzrEmVdzPMdw==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.10.4", @@ -1216,9 +1208,9 @@ } }, "@rollup/plugin-commonjs": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.0.0.tgz", - "integrity": "sha512-/omBIJG1nHQc+bgkYDuLpb/V08QyutP9amOrJRUSlYJZP+b/68gM//D8sxJe3Yry2QnYIr3QjR3x4AlxJEN3GA==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", + "integrity": "sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -1240,9 +1232,9 @@ } }, "@rollup/plugin-node-resolve": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.0.1.tgz", - "integrity": "sha512-ltlsj/4Bhwwhb+Nb5xCz/6vieuEj2/BAkkqVIKmZwC7pIdl8srmgmglE4S0jFlZa32K4qvdQ6NHdmpRKD/LwoQ==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.1.1.tgz", + "integrity": "sha512-zlBXR4eRS+2m79TsUZWhsd0slrHUYdRx4JF+aVQm+MI0wsKdlpC2vlDVjmlGvtZY1vsefOT9w3JxvmWSBei+Lg==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -1284,17 +1276,6 @@ "magic-string": "^0.25.7" } }, - "@rollup/plugin-url": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-url/-/plugin-url-6.0.0.tgz", - "integrity": "sha512-UHGyoo3EbLufCZtXon/baNf75PAYT+Gs/NUgFMx2ZaDQ314PKNP0pJTrpbhDqFkZADzpkqJWyQOfJ2Nd6Qk4TA==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "make-dir": "^3.1.0", - "mime": "^2.4.6" - } - }, "@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", @@ -1495,6 +1476,17 @@ } } }, + "clipboard": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", + "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", + "optional": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1516,6 +1508,12 @@ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", "dev": true }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1601,7 +1599,8 @@ "deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true }, "define-properties": { "version": "1.1.3", @@ -1612,10 +1611,11 @@ "object-keys": "^1.0.12" } }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "optional": true }, "electron-to-chromium": { "version": "1.3.633", @@ -1674,9 +1674,10 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "estree-walker": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.1.tgz", - "integrity": "sha512-tF0hv+Yi2Ot1cwj9eYHtxC0jB9bmjacjQs6ZBTj82H8JwUywFuc+7E83NWfNMwHXZc11mjfFcVXPe9gEP4B8dg==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true }, "esutils": { "version": "2.0.3", @@ -1684,11 +1685,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "fast-memoize": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", - "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==" - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1696,9 +1692,9 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", + "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", "dev": true, "optional": true }, @@ -1745,21 +1741,20 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, - "globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==" - }, - "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" - }, "golden-fleece": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/golden-fleece/-/golden-fleece-1.0.9.tgz", "integrity": "sha512-YSwLaGMOgSBx9roJlNLL12c+FRiw7VECphinc6mGucphc/ZxTHgdEz6gmJqH6NOzYEd/yr64hwjom5pZ+tJVpg==" }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "optional": true, + "requires": { + "delegate": "^3.1.2" + } + }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -1817,14 +1812,6 @@ "param-case": "^2.1.1", "relateurl": "^0.2.7", "uglify-js": "^3.5.1" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } } }, "http-link-header": { @@ -1849,25 +1836,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "intl-messageformat": { - "version": "9.3.20", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.20.tgz", - "integrity": "sha512-jmpjYHE076J/0CIofrPhtUC4LfmsAhuv4JMQxytl2KJd2bim+3+gQJh+Z1vyHUzcj4fIHdt388ZGchb8f0NwOA==", - "requires": { - "fast-memoize": "^2.5.2", - "intl-messageformat-parser": "6.0.18", - "tslib": "^2.0.1" - } - }, - "intl-messageformat-parser": { - "version": "6.0.18", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.18.tgz", - "integrity": "sha512-vLjACEunfi5uSUCWFLOR4PXQ9DGLpED3tM7o9zsYsOvjl0VIheoxyG0WZXnsnhn+S+Zu158M6CkuHXeNZfKRRg==", - "requires": { - "@formatjs/ecma402-abstract": "1.5.0", - "tslib": "^2.0.1" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2033,27 +2001,10 @@ "sourcemap-codec": "^1.4.4" } }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "marked": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.7.tgz", - "integrity": "sha512-No11hFYcXr/zkBvL6qFmAp1z6BKY3zqLMHny/JN/ey+al7qwCM2+CMBL9BOgqMxZU36fz4cCWfn2poWIf7QRXA==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.8.tgz", + "integrity": "sha512-lzmFjGnzWHkmbk85q/ILZjFoHHJIQGF+SxGEfIdGk/XhiTPhqGs37gbru6Kkd48diJnEyYwnG67nru0Z2gQtuQ==" }, "matchit": { "version": "1.1.0", @@ -2076,9 +2027,9 @@ "dev": true }, "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz", + "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag==" }, "mime-db": { "version": "1.44.0", @@ -2108,11 +2059,6 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "mri": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", - "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==" - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2284,6 +2230,14 @@ "trouter": "^2.0.1" } }, + "prismjs": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", + "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", + "requires": { + "clipboard": "^2.0.0" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2394,23 +2348,22 @@ } }, "rollup": { - "version": "2.35.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.35.1.tgz", - "integrity": "sha512-q5KxEyWpprAIcainhVy6HfRttD9kutQpHbeqDTWnqAFNJotiojetK6uqmcydNMymBEtC4I8bCYR+J3mTMqeaUA==", + "version": "2.38.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.4.tgz", + "integrity": "sha512-B0LcJhjiwKkTl79aGVF/u5KdzsH8IylVfV56Ut6c9ouWLJcUK17T83aZBetNYSnZtXf2OHD4+2PbmRW+Fp5ulg==", "dev": true, "requires": { - "fsevents": "~2.1.2" + "fsevents": "~2.3.1" } }, "rollup-plugin-svelte": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-6.1.1.tgz", - "integrity": "sha512-ijnm0pH1ScrY4uxwaNXBpNVejVzpL2769hIEbAlnqNUWZrffLspu5/k9/l/Wsj3NrEHLQ6wCKGagVJonyfN7ow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz", + "integrity": "sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==", "dev": true, "requires": { "require-relative": "^0.8.7", - "rollup-pluginutils": "^2.8.2", - "sourcemap-codec": "^1.4.8" + "rollup-pluginutils": "^2.8.2" } }, "rollup-plugin-terser": { @@ -2442,14 +2395,6 @@ } } }, - "sade": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz", - "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==", - "requires": { - "mri": "^1.1.0" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2477,6 +2422,12 @@ } } }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "optional": true + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -2514,15 +2465,15 @@ "dev": true }, "shimport": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/shimport/-/shimport-2.0.4.tgz", - "integrity": "sha512-5YOyQqYkOFSkPFnpS87De6BYzDiZBc8FS4/aTuGZiST+WmXSwWRoaNRHqyVOeEpSx9wlgYWg9WYfCuzD/11/qA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/shimport/-/shimport-2.0.5.tgz", + "integrity": "sha512-H2FeQyImK4CFhGG1wVhHEB1hASWz+WQK6t2gMP5lk+b0PW30XSrsryDONDBwF1n6hBKsmbr0REfTinaNdEkcPQ==", "dev": true }, "sirv": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.10.tgz", - "integrity": "sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.11.tgz", + "integrity": "sha512-SR36i3/LSWja7AJNRBz4fF/Xjpn7lQFI30tZ434dIy+bitLYSP+ZEenHg36i23V2SGEz+kqjksg0uOGZ5LPiqg==", "requires": { "@polka/url": "^1.0.0-next.9", "mime": "^2.3.1", @@ -2694,24 +2645,11 @@ } }, "svelte": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.31.0.tgz", - "integrity": "sha512-r+n8UJkDqoQm1b+3tA3Lh6mHXKpcfOSOuEuIo5gE2W9wQYi64RYX/qE6CZBDDsP/H4M+N426JwY7XGH4xASvGQ==", + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.32.1.tgz", + "integrity": "sha512-j1KmD2ZOU0RGq1/STDXjwfh0/eJ/Deh2NXyuz1bpR9eOcz9yImn4CGxXdbSAN7cMTm9a7IyPUIbuBCzu/pXK0g==", "dev": true }, - "svelte-i18n": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.3.0.tgz", - "integrity": "sha512-2pIfd4G8QfB87udPugt6g2Wl/qZ0kIB60+nQKHdon7d7aFzY5qsYT2NqQgK7dnVO2Gu0R8i2kSsNzsaVSk5UsA==", - "requires": { - "deepmerge": "^4.2.2", - "dlv": "^1.1.3", - "estree-walker": "^2.0.1", - "intl-messageformat": "^9.3.15", - "sade": "^1.7.4", - "tiny-glob": "^0.2.6" - } - }, "terser": { "version": "5.3.8", "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.8.tgz", @@ -2737,14 +2675,11 @@ } } }, - "tiny-glob": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.8.tgz", - "integrity": "sha512-vkQP7qOslq63XRX9kMswlby99kyO5OvKptw7AMwBVMjXEI7Tb61eoI5DydyEMOseyGS5anDN1VPoVxEvH01q8w==", - "requires": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "optional": true }, "to-fast-properties": { "version": "2.0.0", @@ -2765,15 +2700,10 @@ "matchit": "^1.0.0" } }, - "tslib": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" - }, "uglify-js": { - "version": "3.11.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.5.tgz", - "integrity": "sha512-btvv/baMqe7HxP7zJSF7Uc16h1mSfuuSplT0/qdjxseesDU+yYzH33eHBH+eMdeRXwujXspaCTooWHQVVBh09w==", + "version": "3.12.6", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.6.tgz", + "integrity": "sha512-aqWHe3DfQmZUDGWBbabZ2eQnJlQd1fKlMUu7gV+MiTuDzdgDw31bI3wA2jLLsV/hNcDP26IfyEgSVoft5+0SVw==", "dev": true }, "unicode-canonical-property-names-ecmascript": { diff --git a/site/package.json b/site/package.json index 723238fa1c..fb837018aa 100644 --- a/site/package.json +++ b/site/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "sapper dev", "build": "sapper build --legacy", - "export": "sapper export --legacy", + "export": "sapper export --legacy --concurrent 1", "start": "node __sapper__/build", "cy:run": "cypress run", "cy:open": "cypress open", @@ -17,11 +17,11 @@ "@sindresorhus/slugify": "^1.1.0", "compression": "^1.7.4", "golden-fleece": "^1.0.9", - "highlight.js": "^10.3.1", - "marked": "^1.2.2", + "highlight.js": "^10.5.0", + "marked": "^1.2.8", "polka": "^0.5.2", - "svelte-i18n": "^3.1.0", - "sirv": "^1.0.7" + "prismjs": "^1.23.0", + "sirv": "^1.0.11" }, "devDependencies": { "@babel/core": "^7.12.3", @@ -29,17 +29,16 @@ "@babel/plugin-transform-runtime": "^7.9.0", "@babel/preset-env": "^7.12.1", "@babel/runtime": "^7.12.1", - "@rollup/plugin-babel": "^5.0.0", - "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-babel": "^5.2.3", + "@rollup/plugin-commonjs": "^17.1.0", "@rollup/plugin-json": "^4.0.3", - "@rollup/plugin-node-resolve": "^11.0.1", + "@rollup/plugin-node-resolve": "^11.1.1", "@rollup/plugin-replace": "^2.2.0", - "@rollup/plugin-url": "^6.0.0", "npm-run-all": "^4.1.5", - "rollup": "^2.32.1", - "rollup-plugin-svelte": "^6.1.0", + "rollup": "^2.38.4", + "rollup-plugin-svelte": "^7.0.0", "rollup-plugin-terser": "^7.0.2", "sapper": "^0.28.10", - "svelte": "^3.31.0" + "svelte": "^3.32.1" } } diff --git a/site/quickstarts/000-vue.md b/site/quickstarts/000-vue.md new file mode 100644 index 0000000000..783e955038 --- /dev/null +++ b/site/quickstarts/000-vue.md @@ -0,0 +1,13 @@ +--- +title: Vue +subtitle: Web Apps +img: tech/vue.svg +date: 29 January 2021 +readingtime: 5min +route: vue +visible: false +--- + +This Integration guide shows you our recommended way on how to integrate **ZITADEL** into your Vue Application. +It demonstrates how to add a user login and fetch some data from the user info endpoint. +This Guide is using our [Vue]() template app as reference \ No newline at end of file diff --git a/site/quickstarts/100-angular.md b/site/quickstarts/100-angular.md new file mode 100644 index 0000000000..b304934ec3 --- /dev/null +++ b/site/quickstarts/100-angular.md @@ -0,0 +1,13 @@ +--- +title: Angular +subtitle: Web Apps +img: tech/angular.svg +date: 29 January 2021 +readingtime: 6min +route: angular +visible: true +--- + + +This Integration guide shows you the recommended way on how to integrate **ZITADEL** into your Angular Application. +It demonstrates how to add a user login and fetch some data from the user info endpoint. \ No newline at end of file diff --git a/site/quickstarts/200-flutter.md b/site/quickstarts/200-flutter.md new file mode 100644 index 0000000000..f8840159c8 --- /dev/null +++ b/site/quickstarts/200-flutter.md @@ -0,0 +1,12 @@ +--- +title: Dart / Flutter +subtitle: Native Apps +img: tech/flutter.svg +date: 29 January 2021 +readingtime: 15min +route: flutter +visible: false +--- + +This Integration guide shows you the recommended way on how to integrate **ZITADEL** into your Flutter Application. +This guide is using the Flutter framework and contains additional info on how to configure Android as well as IOS Apps. \ No newline at end of file diff --git a/site/quickstarts/300-go.md b/site/quickstarts/300-go.md new file mode 100644 index 0000000000..8dfde8556d --- /dev/null +++ b/site/quickstarts/300-go.md @@ -0,0 +1,12 @@ +--- +title: Go +subtitle: Backend +img: tech/golang.svg +date: 29 January 2021 +readingtime: 5min +route: go +visible: true +--- + + +This Integration guide shows you the recommended way on how to integrate **ZITADEL** into your Go Backend. diff --git a/site/quickstarts/400-aspnet.md b/site/quickstarts/400-aspnet.md new file mode 100644 index 0000000000..96e50ac5ec --- /dev/null +++ b/site/quickstarts/400-aspnet.md @@ -0,0 +1,12 @@ +--- +title: ASP.NET +subtitle: Backend +img: tech/net.svg +date: 29 January 2021 +readingtime: 5min +route: aspnet +visible: true +--- + +This Integration guide shows you how to access **ZITADEL** for authentication and authorization in your ASP.NET Backend. +This Guide is based on our [ZITADEL NET](https://github.com/caos/zitadel-net) Library. diff --git a/site/quickstarts/500-OAuth2-Proxy.md b/site/quickstarts/500-OAuth2-Proxy.md new file mode 100644 index 0000000000..0d48f73df3 --- /dev/null +++ b/site/quickstarts/500-OAuth2-Proxy.md @@ -0,0 +1,11 @@ +--- +title: OAuth2 Proxy +subtitle: Proxy +img: tech/oauth2-proxy.svg +date: 08 February 2021 +readingtime: 3min +route: oauth2-proxy +visible: true +--- + +With this quickstart you will learn how to use a OAuth 2.0 Proxy in conjuction with **ZITADEL**. \ No newline at end of file diff --git a/site/rollup.config.js b/site/rollup.config.js index 418d18cdcd..1c9969307e 100644 --- a/site/rollup.config.js +++ b/site/rollup.config.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line import/no-named-as-default import babel from '@rollup/plugin-babel'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; @@ -13,20 +14,10 @@ const mode = process.env.NODE_ENV; const dev = mode === 'development'; const legacy = !!process.env.SAPPER_LEGACY_BUILD; -const onwarn = (warning, onwarn) => { - if ( - (warning.code === 'CIRCULAR_DEPENDENCY' && - /[/\\]@sapper[/\\]/.test(warning.message)) - ) { - return; - } - - if (warning.code === 'THIS_IS_UNDEFINED') { - return; - } - +const onwarn = (warning, onwarn) => + (warning.code === 'MISSING_EXPORT' && /'preload'/.test(warning.message)) || + (warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message)) || onwarn(warning); -}; export default { client: { @@ -38,9 +29,10 @@ export default { 'process.env.NODE_ENV': JSON.stringify(mode) }), svelte({ - dev, - hydratable: true, - emitCss: true + compilerOptions: { + dev, + hydratable: true + } }), resolve({ browser: true, @@ -68,11 +60,11 @@ export default { !dev && terser({ module: true }), - json() ], + preserveEntrySignatures: false, - onwarn, + onwarn }, server: { @@ -84,8 +76,12 @@ export default { 'process.env.NODE_ENV': JSON.stringify(mode) }), svelte({ - generate: 'ssr', - dev + compilerOptions: { + dev, + generate: 'ssr', + hydratable: true + }, + emitCss: false }), resolve({ dedupe: ['svelte'] @@ -96,8 +92,9 @@ export default { external: Object.keys(pkg.dependencies).concat( require('module').builtinModules || Object.keys(process.binding('natives')) ), + preserveEntrySignatures: 'strict', - onwarn, + onwarn }, serviceworker: { @@ -110,9 +107,11 @@ export default { 'process.env.NODE_ENV': JSON.stringify(mode) }), commonjs(), + // json(), !dev && terser() ], + preserveEntrySignatures: false, - onwarn, - }, -}; + onwarn + } +}; \ No newline at end of file diff --git a/site/src/client.js b/site/src/client.js index fb03b5816a..3964c2e1cf 100644 --- a/site/src/client.js +++ b/site/src/client.js @@ -2,13 +2,6 @@ import '../static/base.css'; import * as sapper from '@sapper/app'; -import { startClient } from './i18n.js'; -import { initPhotoSwipeFromDOM } from './utils/photoswipe.js'; - -startClient(); - -initPhotoSwipeFromDOM('.zitadel-gallery'); - sapper.start({ target: document.querySelector('#sapper') -}); +}); \ No newline at end of file diff --git a/site/src/components/CodeTable.svelte b/site/src/components/CodeTable.svelte deleted file mode 100644 index 3ad524cdd8..0000000000 --- a/site/src/components/CodeTable.svelte +++ /dev/null @@ -1,121 +0,0 @@ - - - - -
- - - - -
- {#each tabs as { lang, content }, i} -
- {content} -
- {/each} -
-
diff --git a/site/src/components/Docs.svelte b/site/src/components/Docs.svelte index e1b6bf5fb6..d2b000fed8 100644 --- a/site/src/components/Docs.svelte +++ b/site/src/components/Docs.svelte @@ -1,8 +1,7 @@ - - - -
- zitadel logo - - -
- - -
- - More about ZITADEL - -
- -{#if menuOpen} - -{/if} \ No newline at end of file diff --git a/site/src/components/GuideContents.svelte b/site/src/components/GuideContents.svelte index f0eb4157a1..9ad418312f 100644 --- a/site/src/components/GuideContents.svelte +++ b/site/src/components/GuideContents.svelte @@ -1,8 +1,7 @@ - - - - - -
- {#each LANGUAGES as lang} - - {/each} -
diff --git a/site/src/components/Nav.svelte b/site/src/components/Nav.svelte index 7a3486076a..afbffed6b9 100644 --- a/site/src/components/Nav.svelte +++ b/site/src/components/Nav.svelte @@ -1,17 +1,28 @@ {#if external} - +
  • {:else} - + {#if prefetch} +
  • + {:else} +
  • + {/if} {/if} \ No newline at end of file diff --git a/site/src/components/SearchSelector.svelte b/site/src/components/SearchSelector.svelte index 84dc42b055..daf89ecfa4 100644 --- a/site/src/components/SearchSelector.svelte +++ b/site/src/components/SearchSelector.svelte @@ -1,6 +1,5 @@ + +
    -
    + + \ No newline at end of file diff --git a/site/src/routes/index.svelte b/site/src/routes/index.svelte index 3d38b01757..8aa75e8570 100644 --- a/site/src/routes/index.svelte +++ b/site/src/routes/index.svelte @@ -1,33 +1,11 @@ - - @@ -193,165 +149,124 @@ ZITADEL • Documentation - {$_('title')} - + ZITADEL • Documentation + - - + + - - + + - -
    -
    - -

    {$_('languagealsoavailable')}: - {#each LANGUAGES as lang} - {#if lang != $locale} - {lang == 'de'? 'Deutsch (WIP)' : 'English'} - {/if} - {/each} -

    -
    -

    {$_('title')}

    -

    {$_('description')}

    -

    {$_('description2')}

    -

    {$_('description3')}

    +

    ZITADEL Identity and Access Management

    +

    Most applications need to know the identity of a user allowing to securely store user data in the cloud and provide the same personalised experience across all of the user's devices.

    +

    ZITADEL's authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users in your application. It supports authentication using passwords and applies additional security with the help of a second factor, for example OTP, to ensure a safe and secure access.

    +

    ZITADEL's authentication leverages industry standards like OAuth 2.0 and OpenID Connect so that it can be easily integrated in your custom backend.

    caos logo
    -

    {$_('startlink')}

    -

    {$_('startlink_desc')}

    +

    Follow this guide to get started with ZITADEL in general.

    - - - {#if $locale == 'en'} - - {:else if $locale == 'de'} - - {/if}
    -

    {$_('integratelink')}

    -

    {$_('integratelink_desc')}

    - {$_('learnmore')} +

    Quickstarts

    +

    Learn how to integrate your applications and build secure workflows and APIs with ZITADEL

    - {#if $locale == 'en'} -

    {$_('inthissection')}

    - - {/if} +
    + react + angular + flutter + golang + aspnet +
    + Learn more + +

    In this section

    +
    - Develop
    -

    {$_('administratelink')}

    -

    {$_('administratelink_desc')}

    - {$_('learnmore')} +

    Administration

    +

    Learn how to manage your data and role associations in ZITADEL

    + Learn more -

    {$_('inthissection')}

    +

    In this section

    - {#if $locale == 'en'} - - {:else if $locale == 'de'} - - {/if} -
    - Develop -
    - -
    -
    -

    {$_('developlink')}

    -

    {$_('developlink_desc')}

    - {$_('learnmore')} - -

    {$_('inthissection')}

    - - {#if $locale == 'en'} - - {:else if $locale == 'de'} - - {/if} +
    -

    {$_('docslink')}

    -

    {$_('docslink_desc')}

    - {$_('learnmore')} +

    APIs

    + +

    Learn more about our APIs and how to integrate them in your apps.

    + Learn more -

    {$_('inthissection')}

    - {#if $locale == 'en'} - - {:else if $locale == 'de'} - - {/if} +

    In this section

    + +
    -

    {$_('uselink')}

    -

    {$_('uselink_desc')}

    - {$_('learnmore')} -
    +

    Architecture and Technologies

    + +

    Learn more about engineering and design principles, ZITADELs architecture and used technologies.

    + Learn more - Develop +

    In this section

    + +
    +
    + +
    +
    +

    User Manuals

    + + +

    Follow this guide to get started with ZITADEL as a user.

    + Learn more +
    diff --git a/site/src/routes/quickstarts/_quickstarts.js b/site/src/routes/quickstarts/_quickstarts.js new file mode 100644 index 0000000000..350f64f698 --- /dev/null +++ b/site/src/routes/quickstarts/_quickstarts.js @@ -0,0 +1,59 @@ +import fs from 'fs'; +import marked from 'marked'; +import path from 'path'; + +import { SLUG_PRESERVE_UNICODE } from '../../../config'; +import { highlight } from '../../utils/highlight'; +import { extract_frontmatter, link_renderer } from '../../utils/markdown.js'; +import { makeSlugProcessor } from '../../utils/slug'; + +const makeSlug = makeSlugProcessor(SLUG_PRESERVE_UNICODE); + +export default function get_quickstarts() { + return fs + .readdirSync('quickstarts') + .map(file => { + if (path.extname(file) !== '.md') return; + + const match = /^([0-9]+)-(.+)\.md$/.exec(file); + if (!match) throw new Error(`Invalid filename '${file}'`); + + const [, order, slug] = match; + + const markdown = fs.readFileSync(`quickstarts/${file}`, 'utf-8'); + + const { content, metadata } = extract_frontmatter(markdown); + + const renderer = new marked.Renderer(); + + renderer.link = link_renderer; + + renderer.code = highlight; + + renderer.heading = (text, level, rawtext) => { + const fragment = makeSlug(rawtext); + + return ` + + + + ${text} + `; + }; + + const answer = marked( + content.replace(/^\t+/gm, match => match.split('\t').join(' ')), + { renderer } + ); + + const fragment = makeSlug(slug); + + return { + fragment, + order, + answer, + metadata + }; + }) + .sort((a, b) => a.order - b.order); +} \ No newline at end of file diff --git a/site/src/routes/quickstarts/index.json.js b/site/src/routes/quickstarts/index.json.js new file mode 100644 index 0000000000..5faf96cf79 --- /dev/null +++ b/site/src/routes/quickstarts/index.json.js @@ -0,0 +1,25 @@ +import send from '@polka/send'; + +import get_quickstarts from './_quickstarts.js'; + +let json; + +export function get(req, res) { + if (!json || process.env.NODE_ENV !== 'production') { + const qss = get_quickstarts() + .map(qs => { + return { + fragment: qs.fragment, + answer: qs.answer, + metadata: qs.metadata + }; + }); + + json = JSON.stringify(qss); + } + + send(res, 200, json, { + 'Content-Type': 'application/json', + 'Cache-Control': `max-age=${5 * 60 * 1e3}` // 5 minutes + }); +} \ No newline at end of file diff --git a/site/src/routes/quickstarts/index.svelte b/site/src/routes/quickstarts/index.svelte new file mode 100644 index 0000000000..f0c30fe92a --- /dev/null +++ b/site/src/routes/quickstarts/index.svelte @@ -0,0 +1,154 @@ + + + + + + Quickstarts ZITADEL + + + + + + +
    +

    Quickstarts ZITADEL

    + {#each qss as qs} + {#if qs.metadata.visible == 'true'} +
    +
    +

    {qs.metadata.subtitle}

    +

    + + + {qs.metadata.title} +

    +

    {@html qs.answer}

    + Read Quickstart +

    {qs.metadata.date} • {qs.metadata.readingtime}

    +
    + article img +
    + {/if} + {/each} +

    See also our Github page ZITADEL for questions regarding the sourcecode.

    + + \ No newline at end of file diff --git a/site/src/server.js b/site/src/server.js index e874066b47..d0e4e1d61f 100644 --- a/site/src/server.js +++ b/site/src/server.js @@ -3,8 +3,6 @@ import compression from 'compression'; import polka from 'polka'; import sirv from 'sirv'; -import { i18nMiddleware } from './i18n.js'; - const { PORT, NODE_ENV } = process.env; const dev = NODE_ENV === 'development'; @@ -12,7 +10,6 @@ polka() .use( compression({ threshold: 0 }), sirv('static', { dev }), - i18nMiddleware(), sapper.middleware() ) .listen(PORT, err => { diff --git a/site/src/service-worker.js b/site/src/service-worker.js index 54c62e2718..fad33e5256 100644 --- a/site/src/service-worker.js +++ b/site/src/service-worker.js @@ -1,5 +1,6 @@ -import { files, routes, shell, timestamp } from '@sapper/service-worker'; +import { files, shell, timestamp } from '@sapper/service-worker'; +// eslint-disable-next-line import/no-unresolved const ASSETS = `cache${timestamp}`; // `shell` is an array of all the files generated by the bundler, @@ -51,12 +52,12 @@ self.addEventListener('fetch', event => { // for pages, you might want to serve a shell `service-worker-index.html` file, // which Sapper has generated for you. It's not right for every // app, but if it's right for yours then uncomment this section - - // if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) { - // event.respondWith(caches.match('/service-worker-index.html')); - // return; - // } - + /* + if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) { + event.respondWith(caches.match('/service-worker-index.html')); + return; + } + */ if (event.request.cache === 'only-if-cached') return; @@ -79,4 +80,4 @@ self.addEventListener('fetch', event => { } }) ); -}); +}); \ No newline at end of file diff --git a/site/src/template.html b/site/src/template.html index 86f49fa5da..8da09bfbf7 100644 --- a/site/src/template.html +++ b/site/src/template.html @@ -98,7 +98,7 @@ + initialise the router --> %sapper.scripts% diff --git a/site/src/utils/generate_code_tabs.js b/site/src/utils/generate_code_tabs.js deleted file mode 100644 index d404310ab4..0000000000 --- a/site/src/utils/generate_code_tabs.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function generate_code_tabs(dirpath, dir) { - -} \ No newline at end of file diff --git a/site/src/utils/generate_docs.js b/site/src/utils/generate_docs.js index 572507daeb..739801951c 100644 --- a/site/src/utils/generate_docs.js +++ b/site/src/utils/generate_docs.js @@ -20,18 +20,16 @@ const block_types = [ 'tablecell' ]; -export default function generate_docs(dirpath, dir, lang) { +export default function generate_docs(dirpath, dir) { const make_slug = make_session_slug_processor({ separator: SLUG_SEPARATOR, preserve_unicode: SLUG_PRESERVE_UNICODE }); - console.log('using language: ' + lang); - return fs .readdirSync(`${dirpath}${dir}`) .filter((file) => { - return file[0] !== '.' && path.extname(file) === '.md' && file.endsWith(`.${lang}.md`); + return file[0] !== '.' && path.extname(file) === '.md' && file.endsWith(`.md`); }) .map((file) => { const markdown = fs.readFileSync(`${dirpath}${dir}/${file}`, 'utf-8'); @@ -45,7 +43,7 @@ export default function generate_docs(dirpath, dir, lang) { renderer.link = link_renderer; - renderer.hr = (str) => { + renderer.hr = () => { block_open = true; return '
    '; @@ -65,7 +63,9 @@ export default function generate_docs(dirpath, dir, lang) { }; renderer.code = (source, lang) => { - source = source.replace(/^ +/gm, (match) => match.split(' ').join('\t')); + source = source.replace(/^ +/gm, match => + match.split(' ').join('\t') + ); const lines = source.split('\n'); @@ -90,6 +90,11 @@ export default function generate_docs(dirpath, dir, lang) { const plang = langs[lang]; const { value: highlighted } = hljs.highlight(lang, source); + // const highlighted = PrismJS.highlight( + // source, + // PrismJS.languages[plang], + // lang + // ); const html = `
    ${prefix}
    ${highlighted}
    `; @@ -101,16 +106,17 @@ export default function generate_docs(dirpath, dir, lang) { return html; }; - // const slugger = new marked.Slugger(); renderer.heading = (text, level, rawtext) => { const slug = level <= 4 && make_slug(rawtext); if (level === 3 || level === 4) { - const title = text.replace(/<\/?code>/g, '').replace(/\.(\w+)(\((.+)?\))?/, (m, $1, $2, $3) => { - if ($3) return `.${$1}(...)`; - if ($2) return `.${$1}()`; - return `.${$1}`; - }); + const title = text + .replace(/<\/?code>/g, '') + .replace(/\.(\w+)(\((.+)?\))?/, (m, $1, $2, $3) => { + if ($3) return `.${$1}(...)`; + if ($2) return `.${$1}()`; + return `.${$1}`; + }); subsections.push({ slug, title, level }); } @@ -118,12 +124,12 @@ export default function generate_docs(dirpath, dir, lang) { return ` 4 ? 'data-scrollignore' : ''}> - + ${text} `; }; - block_types.forEach((type) => { + block_types.forEach(type => { const fn = renderer[type]; renderer[type] = function () { return fn.apply(this, arguments); @@ -141,4 +147,4 @@ export default function generate_docs(dirpath, dir, lang) { file }; }); -} +} \ No newline at end of file diff --git a/site/src/utils/generate_seo.js b/site/src/utils/generate_seo.js index 5663f4110d..d7490fc3d0 100644 --- a/site/src/utils/generate_seo.js +++ b/site/src/utils/generate_seo.js @@ -1,8 +1,8 @@ import fs from 'fs'; -export default function generate_docs(dirpath, dir, lang) { +export default function generate_seo(dirpath, dir) { try { - return fs.readFileSync(`${dirpath}${dir}/seo_${lang}.html`, 'utf-8'); + return fs.readFileSync(`${dirpath}${dir}/seo.html`, 'utf-8'); } catch (error) { return ''; } diff --git a/site/src/utils/highlight.js b/site/src/utils/highlight.js new file mode 100644 index 0000000000..21e803d5c7 --- /dev/null +++ b/site/src/utils/highlight.js @@ -0,0 +1,16 @@ +import 'prismjs/components/prism-bash'; + +import PrismJS from 'prismjs'; + +import { langs } from './markdown.js'; + +export function highlight(source, lang) { + const plang = langs[lang] || ''; + const highlighted = plang ? PrismJS.highlight( + source, + PrismJS.languages[plang], + lang + ) : source.replace(/[&<>]/g, c => ({ '&': '&', '<': '<', '>': '>' })[c]); + + return `
    ${highlighted}
    `; +} \ No newline at end of file diff --git a/site/src/utils/language_extractor.js b/site/src/utils/language_extractor.js deleted file mode 100644 index c561ff02fd..0000000000 --- a/site/src/utils/language_extractor.js +++ /dev/null @@ -1,21 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -export default function extract_languages(dirpath, dir) { - - const detectedLocales = fs.readdirSync(`${dirpath}${dir}`) - .filter(file => path.extname(file) == '.md') - .map((file) => { - file = file.replace(path.extname(file), ''); - const arr = file.split('.'); - const locale = arr.length ? arr[arr.length - 1] : null; - if (locale) { - return locale; - } - }).filter(locale => locale !== null); - - const redDetectedLocales = [...new Set(detectedLocales)]; - - console.log('detected locales: ' + redDetectedLocales); - return redDetectedLocales; -} diff --git a/site/src/utils/photoswipe.js b/site/src/utils/photoswipe.js index 191d7eaaaf..02a7f52662 100644 --- a/site/src/utils/photoswipe.js +++ b/site/src/utils/photoswipe.js @@ -22,7 +22,8 @@ export function initPhotoSwipeFromDOM(gallerySelector) { linkEl = figureEl.children[0]; // element - size = linkEl.getAttribute('data-size').split('x'); + const dataSize = linkEl.getAttribute('data-size'); + size = dataSize ? dataSize.split('x') : '1920x1080'.split('x'); // create slide object item = { diff --git a/site/src/utils/slug.js b/site/src/utils/slug.js index e68511b4a3..65bf103601 100644 --- a/site/src/utils/slug.js +++ b/site/src/utils/slug.js @@ -1,68 +1,66 @@ import slugify from '@sindresorhus/slugify'; -export const SLUG_PRESERVE_UNICODE = false; -export const SLUG_SEPARATOR = '_'; +import { SLUG_SEPARATOR } from '../../config'; /* url-safe processor */ -export const urlsafeSlugProcessor = (string, opts) => { - const { separator = SLUG_SEPARATOR } = opts || {}; - - return slugify(string, { - customReplacements: [ - // runs before any other transformations +export const urlsafeSlugProcessor = string => + slugify(string, { + customReplacements: [ // runs before any other transformations ['$', 'DOLLAR'], // `$destroy` & co ['-', 'DASH'] // conflicts with `separator` ], - separator, + separator: SLUG_SEPARATOR, decamelize: false, lowercase: false }) .replace(/DOLLAR/g, '$') .replace(/DASH/g, '-'); -}; /* unicode-preserver processor */ const alphaNumRegex = /[a-zA-Z0-9]/; const unicodeRegex = /\p{Letter}/u; -const isNonAlphaNumUnicode = (string) => !alphaNumRegex.test(string) && unicodeRegex.test(string); +const isNonAlphaNumUnicode = + string => !alphaNumRegex.test(string) && unicodeRegex.test(string); -export const unicodeSafeProcessor = (string, opts) => { - const { separator = SLUG_SEPARATOR } = opts || {}; +export const unicodeSafeProcessor = string => + string.split('') + .reduce((accum, char, index, array) => { + const type = isNonAlphaNumUnicode(char) ? 'pass' : 'process'; - return string - .split('') - .reduce( - (accum, char, index, array) => { - const type = isNonAlphaNumUnicode(char) ? 'pass' : 'process'; + if (index === 0) { + accum.current = { type, string: char }; + } else if (type === accum.current.type) { + accum.current.string += char; + } else { + accum.chunks.push(accum.current); + accum.current = { type, string: char }; + } - if (index === 0) { - accum.current = { type, string: char }; - } else if (type === accum.current.type) { - accum.current.string += char; - } else { - accum.chunks.push(accum.current); - accum.current = { type, string: char }; - } + if (index === array.length - 1) { + accum.chunks.push(accum.current); + } - if (index === array.length - 1) { - accum.chunks.push(accum.current); - } - - return accum; - }, - { chunks: [], current: { type: '', string: '' } } - ) - .chunks.reduce((accum, chunk) => { - const processed = chunk.type === 'process' ? urlsafeSlugProcessor(chunk.string) : chunk.string; + return accum; + }, { chunks: [], current: { type: '', string: '' } }) + .chunks + .reduce((accum, chunk) => { + const processed = chunk.type === 'process' + ? urlsafeSlugProcessor(chunk.string) + : chunk.string; processed.length > 0 && accum.push(processed); return accum; }, []) - .join(separator); -}; + .join(SLUG_SEPARATOR); + +/* processor */ + +export const makeSlugProcessor = (preserveUnicode = false) => preserveUnicode + ? unicodeSafeProcessor + : urlsafeSlugProcessor; /* session processor */ @@ -78,7 +76,6 @@ export const make_session_slug_processor = ({ if (seen.has(slug)) throw new Error(`Duplicate slug ${slug}`); seen.add(slug); - return slug; }; }; diff --git a/site/static/base.css b/site/static/base.css index 2bd6886965..4e64846c56 100644 --- a/site/static/base.css +++ b/site/static/base.css @@ -180,7 +180,8 @@ ul { .b, b, strong { - font-weight: 600; + font-weight: 600; + color: white; } tt, @@ -193,8 +194,9 @@ samp { code { position: relative; border-radius: .3em; - white-space: nowrap; - color: var(--text); + white-space: nowrap; + font-size: var(--h6); + /* color: var(--text); */ -webkit-font-smoothing: initial; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; } @@ -223,7 +225,7 @@ li:not(.white) > h2 { blockquote { position: relative; margin: 1.6rem 0 2.4rem; - padding: 2rem 2.4rem 1.8rem 2.4rem; + padding: 1rem 2.4rem 1rem 2.4rem; border-radius: var(--border-r); font-family: var(--font); max-width: var(--linemax); @@ -326,11 +328,6 @@ a:hover { color: var(--flash); } -a:hover { - padding: 0; - border-bottom: 2px solid currentColor; -} - a.no-underline { border-bottom: none; padding: 0; @@ -405,7 +402,7 @@ td, th { text-align: left; border-bottom: 1px solid #eee; - padding: 0.4rem 0.8rem 0.4rem 0; + padding: 4px 8px 4px 0; } td[align='right'] { @@ -594,10 +591,12 @@ input[type="checkbox"]:checked::after { .zitadel-gallery img { width: 100%; object-fit: contain; + border-radius: 4px; } .zitadel-gallery figure { flex-basis: 30%; object-fit: contain; margin: 5px 5px 5px 0; width: 150px; + border-radius: 4px; } diff --git a/site/static/img/accounts_org_register.png b/site/static/img/accounts_org_register.png deleted file mode 100644 index 0bf4e4a5aa2ebac8a2147f9361780074a2c3d004..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73051 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

    bpcQ_X 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 + + + + + + + + + + diff --git a/site/static/tech/flutter.svg b/site/static/tech/flutter.svg new file mode 100644 index 0000000000..b65f8527ee --- /dev/null +++ b/site/static/tech/flutter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/static/tech/golang.svg b/site/static/tech/golang.svg new file mode 100644 index 0000000000..8164d5589e --- /dev/null +++ b/site/static/tech/golang.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/static/tech/net.svg b/site/static/tech/net.svg new file mode 100644 index 0000000000..2bea1985a9 --- /dev/null +++ b/site/static/tech/net.svg @@ -0,0 +1 @@ +layer1 \ No newline at end of file diff --git a/site/static/tech/oauth2-proxy.svg b/site/static/tech/oauth2-proxy.svg new file mode 100644 index 0000000000..4df740f6d3 --- /dev/null +++ b/site/static/tech/oauth2-proxy.svg @@ -0,0 +1 @@ +OAuth2_Proxy_logo_v3 \ No newline at end of file diff --git a/site/static/tech/react.png b/site/static/tech/react.png new file mode 100644 index 0000000000000000000000000000000000000000..430e213a0f82f5d5d5a7c1c9d2f2c71287857a95 GIT binary patch literal 46955 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 )stn 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 U@4ezl*av>lYTBx~%Gdn=2c-7=!T \ No newline at end of file diff --git a/site/static/tech/angular.svg b/site/static/tech/angular.svg new file mode 100644 index 0000000000..96301efe1b --- /dev/null +++ b/site/static/tech/angular.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/site/static/tech/aspnet.png b/site/static/tech/aspnet.png new file mode 100644 index 0000000000000000000000000000000000000000..274660b1e1b738bc0dec7e35445d68066cd964fb GIT binary patch literal 67884 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

    {{ 'PROJECT.ROLE.NAME' | translate }} - {{role.name}} {{ 'APP.NAME' | translate }} + {{app.name}} {{ 'APP.OIDC.APPTYPE' | translate }} + {{'APP.OIDC.APPTYPE'+app?.oidcConfig?.applicationType | translate}}