From 504fe5b761ea33b4232e3babebff18bde07538aa Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Thu, 24 Mar 2022 14:00:24 +0100 Subject: [PATCH] cherry pick changes from main (#3371) * feat: remove exif data from uploaded images (#3221) * feat: remove exif tags from images * feat: remove exif data * feat: remove exif * fix: add preferredLoginName to user grant response (#3271) * chore: log webauthn parse error (#3272) * log error * log error * feat: Help link in privacy policy * fix: convert correct detail data on organization (#3279) * fix: handle empty editor users * fix: add some missing translations (#3291) * fix: org policy translations * fix: metadata event types translation * fix: translations * fix: filter resource owner correctly on project grant members (#3281) * fix: filter resource owner correctly on project grant members * fix: filter resource owner correctly on project grant members * fix: add orgIDs to zitadel permissions request Co-authored-by: fabi * fix: get IAM memberships correctly in MyZitadelPermissions (#3309) * fix: correct login names on auth and notification users (#3349) * fix: correct login names on auth and notification users * fix: migration * fix: handle resource owner in action flows (#3361) * fix merge * fix: exchange exif library (#3366) * fix: exchange exif library * ignore tiffs * requested fixes * feat: Help link in privacy policy Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: fabi --- .../edit-text/edit-text.component.html | 41 +- .../modules/policies/login-texts/helper.ts | 4 - .../privacy-policy.component.html | 50 +- .../privacy-policy.component.scss | 70 ++- .../privacy-policy.component.ts | 134 ++-- .../privacy-policy/privacy-policy.module.ts | 4 +- console/src/assets/i18n/de.json | 1 + console/src/assets/i18n/en.json | 1 + console/src/assets/i18n/it.json | 1 + docs/docs/apis/proto/admin.md | 2 + docs/docs/apis/proto/management.md | 4 + docs/docs/apis/proto/policy.md | 1 + docs/docs/apis/proto/text.md | 1 - docs/docs/apis/proto/user.md | 1 + go.mod | 15 +- go.sum | 32 +- internal/api/assets/asset.go | 33 +- .../grpc/admin/privacy_policy_converter.go | 1 + internal/api/grpc/auth/permission.go | 2 +- internal/api/grpc/auth/user.go | 1 + .../management/policy_privacy_converter.go | 2 + .../management/project_grant_converter.go | 6 +- internal/api/grpc/org/converter.go | 2 +- internal/api/grpc/policy/privacy_policy.go | 1 + internal/api/grpc/text/custom_text.go | 2 - internal/api/grpc/user/user_grant.go | 33 +- internal/api/oidc/auth_request.go | 16 +- .../api/ui/login/external_login_handler.go | 2 +- internal/api/ui/login/jwt_handler.go | 2 +- internal/api/ui/login/renderer.go | 41 +- internal/api/ui/login/static/i18n/de.yaml | 1 - internal/api/ui/login/static/i18n/en.yaml | 1 - internal/api/ui/login/static/i18n/it.yaml | 1 - .../api/ui/login/static/templates/footer.html | 4 +- .../eventsourcing/eventstore/auth_request.go | 8 +- .../repository/eventsourcing/handler/user.go | 55 +- internal/command/custom_login_text.go | 4 - internal/command/custom_login_text_model.go | 8 - internal/command/iam_converter.go | 1 + .../command/iam_custom_login_text_test.go | 32 - internal/command/iam_policy_privacy.go | 4 +- internal/command/iam_policy_privacy_model.go | 6 +- internal/command/iam_policy_privacy_test.go | 24 +- .../command/org_custom_login_text_test.go | 32 - internal/command/org_features_test.go | 12 +- internal/command/org_policy_org_iam.go | 2 +- internal/command/org_policy_privacy.go | 5 +- internal/command/org_policy_privacy_model.go | 6 +- internal/command/org_policy_privacy_test.go | 27 +- internal/command/policy_privacy_model.go | 5 + internal/command/user.go | 4 +- internal/command/user_human.go | 6 +- internal/command/user_human_otp.go | 2 +- internal/domain/custom_login_text.go | 2 - internal/domain/policy_privacy.go | 1 + .../iam/repository/view/model/custom_text.go | 3 - .../eventsourcing/handler/notify_user.go | 54 +- internal/query/custom_text.go | 3 - internal/query/privacy_policy.go | 16 + internal/query/privacy_policy_test.go | 8 +- internal/query/project_grant_member.go | 15 +- internal/query/project_grant_member_test.go | 2 + internal/query/projection/flow.go | 2 + internal/query/projection/flow_test.go | 6 +- internal/query/projection/privacy_policy.go | 6 + .../query/projection/privacy_policy_test.go | 24 +- internal/query/user_grant.go | 73 ++- internal/query/user_grant_test.go | 576 +++++++++++------- internal/query/user_membership.go | 8 + internal/query/zitadel_permission.go | 8 +- internal/repository/iam/policy_privacy.go | 6 +- internal/repository/org/policy_privacy.go | 6 +- internal/repository/policy/policy_privacy.go | 12 +- internal/static/i18n/de.yaml | 12 +- internal/static/i18n/en.yaml | 12 +- internal/static/i18n/it.yaml | 12 +- .../user/repository/view/model/notify_user.go | 6 +- internal/user/repository/view/model/user.go | 6 +- internal/webauthn/webauthn.go | 3 + proto/zitadel/admin.proto | 2 + proto/zitadel/management.proto | 4 + proto/zitadel/policy.proto | 3 +- proto/zitadel/text.proto | 5 +- proto/zitadel/user.proto | 5 + 84 files changed, 1055 insertions(+), 602 deletions(-) diff --git a/console/src/app/modules/edit-text/edit-text.component.html b/console/src/app/modules/edit-text/edit-text.component.html index 83ef871cb1..f4408e1eed 100644 --- a/console/src/app/modules/edit-text/edit-text.component.html +++ b/console/src/app/modules/edit-text/edit-text.component.html @@ -1,31 +1,42 @@ -
+
-
+
- + {{key.key}} - +
- -
- {{chip.key | translate}} - {{chip.value}} - - -
-
+ +
+ {{chip.key | translate}} + {{chip.value}} + + +
+
- - + +
- {{'ACTIONS.RESETTO'| translate }} '{{warnText[key.key]}}' + + {{'ACTIONS.RESETTO'| translate }} '{{warnText[key.key]}}'
diff --git a/console/src/app/modules/policies/login-texts/helper.ts b/console/src/app/modules/policies/login-texts/helper.ts index aea02cadf3..f8d36df188 100644 --- a/console/src/app/modules/policies/login-texts/helper.ts +++ b/console/src/app/modules/policies/login-texts/helper.ts @@ -71,7 +71,6 @@ export function mapRequestValues(map: Partial, req: Req): Req { const r3 = new FooterText(); r3.setHelp(map.footerText?.help ?? ''); - r3.setHelpLink(map.footerText?.helpLink ?? ''); r3.setPrivacyPolicy(map.footerText?.privacyPolicy ?? ''); r3.setTos(map.footerText?.tos ?? ''); req.setFooterText(r3); @@ -102,7 +101,6 @@ export function mapRequestValues(map: Partial, req: Req): Req { r6.setU2fOption(map.initMfaPromptText?.otpOption ?? ''); req.setInitMfaPromptText(r6); - const r7 = new InitMFAU2FScreenText(); r7.setDescription(map.initMfaU2fText?.description ?? ''); r7.setErrorRetry(map.initMfaU2fText?.errorRetry ?? ''); @@ -112,7 +110,6 @@ export function mapRequestValues(map: Partial, req: Req): Req { r7.setTokenNameLabel(map.initMfaU2fText?.tokenNameLabel ?? ''); req.setInitMfaU2fText(r7); - const r8 = new InitPasswordDoneScreenText(); r8.setCancelButtonText(map.initPasswordDoneText?.cancelButtonText ?? ''); r8.setDescription(map.initPasswordDoneText?.description ?? ''); @@ -348,7 +345,6 @@ export function mapRequestValues(map: Partial, req: Req): Req { r32.setTokenNameLabel(map.passwordlessRegistrationText?.tokenNameLabel ?? ''); req.setPasswordlessRegistrationText(r32); - const r33 = new PasswordlessScreenText(); r33.setDescription(map.passwordlessText?.description ?? ''); r33.setErrorRetry(map.passwordlessText?.errorRetry ?? ''); diff --git a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html index 31f4f5bae1..a9f9799f19 100644 --- a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html +++ b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html @@ -1,35 +1,59 @@ + [title]="'POLICY.PRIVACY_POLICY.TITLE' | translate" [description]="'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate"> {{'POLICY.DEFAULTLABEL' | translate}} - +
-
+
- - {{ 'POLICY.PRIVACY_POLICY.TOSLINK' | translate }} - + + {{ 'POLICY.PRIVACY_POLICY.TOSLINK' | translate }} + + - + {{ 'POLICY.PRIVACY_POLICY.POLICYLINK' | translate }} + + + + + {{ 'POLICY.PRIVACY_POLICY.HELPLINK' | translate }} + +
- - + +
- + + + +
+
+ {{LANGPLACEHOLDER}} + + +
+
+
diff --git a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.scss b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.scss index 8a18e531cf..5814f31299 100644 --- a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.scss +++ b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.scss @@ -1,26 +1,26 @@ .spinner-wr { - margin: .5rem 0; + margin: 0.5rem 0; } .top-actions { display: flex; - margin: 0 -.5rem; + margin: 0 -0.5rem; flex-wrap: wrap; .keys { flex: 1; - margin: 0 .5rem; + margin: 0 0.5rem; min-width: 150px; } .language { - margin: 0 .5rem; + margin: 0 0.5rem; min-width: 150px; .lighter { font-size: 12px; color: var(--grey); - padding: 0 .5rem; + padding: 0 0.5rem; } } } @@ -34,40 +34,66 @@ padding-top: 1rem; } -.chips { - display: flex; - margin: 0 -.25rem; - - .chip { - border-radius: 50vw; - padding: 2px .5rem; - font-size: 12px; - background: #cbf4c9; - color: #0e6245; - margin: .25rem; +.privacy-policy-formfield { + .chips { display: flex; - align-items: center; - justify-content: center; - z-index: 10; + flex-wrap: wrap; + opacity: 0; + margin: 0 -0.25rem; + transition: all 0.2s ease; + + .chip { + border-radius: 50vw; + padding: 4px 0.5rem; + font-size: 12px; + background: #5282c1; + color: white; + margin: 0.25rem; + display: flex; + align-items: center; + justify-content: center; + z-index: 10; + + * { + transition: all 0.2s ease; + } + + i { + opacity: 0.5; + font-size: 1.1rem; + margin-left: 0.5rem; + } + + .key { + display: inline-block; + } + } + } + + &.cnsl-focused { + .chips { + opacity: 1; + cursor: copy; + } } } .divider { width: 100%; height: 1px; - background-color: rgba(#81868a, .5); + background-color: rgba(#81868a, 0.5); margin: 1.5rem 0 0 0; } .actions { display: flex; justify-content: flex-end; - margin: 0 -.25rem; + margin: 0 -0.25rem; .save-button, .reset-button { display: block; - margin: 0 .25rem 3rem .25rem; + margin: 0 0.25rem 3rem 0.25rem; } .reset-button { diff --git a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.ts b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.ts index 91220f09ac..5cdaf2be52 100644 --- a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.ts +++ b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.ts @@ -37,11 +37,14 @@ export class PrivacyPolicyComponent implements OnDestroy { public nextLinks: CnslLinks[] = []; private sub: Subscription = new Subscription(); - public privacyPolicy!: PrivacyPolicy.AsObject; + public privacyPolicy: PrivacyPolicy.AsObject | undefined = undefined; public form!: FormGroup; public currentPolicy: GridPolicy = PRIVACY_POLICY; public InfoSectionType: any = InfoSectionType; + public LANGPLACEHOLDER: string = '{{.Lang}}'; + public copied: string = ''; + constructor( private route: ActivatedRoute, private injector: Injector, @@ -49,70 +52,108 @@ export class PrivacyPolicyComponent implements OnDestroy { private toast: ToastService, private fb: FormBuilder, ) { - this.form = this.fb.group({ tosLink: ['', []], privacyLink: ['', []], + helpLink: ['', []], }); - this.route.data.pipe(switchMap(data => { - this.serviceType = data.serviceType; - switch (this.serviceType) { - case PolicyComponentServiceType.MGMT: - this.service = this.injector.get(ManagementService as Type); - this.loadData(); - break; - case PolicyComponentServiceType.ADMIN: - this.service = this.injector.get(AdminService as Type); - this.loadData(); - break; - } + this.route.data + .pipe( + switchMap((data) => { + this.serviceType = data.serviceType; + switch (this.serviceType) { + case PolicyComponentServiceType.MGMT: + this.service = this.injector.get(ManagementService as Type); + this.loadData(); + break; + case PolicyComponentServiceType.ADMIN: + this.service = this.injector.get(AdminService as Type); + this.loadData(); + break; + } - return this.route.params; - })).subscribe(); + return this.route.params; + }), + ) + .subscribe(); + } + + public addChip(formControlName: string, value: string): void { + const c = this.form.get(formControlName)?.value; + this.form.get(formControlName)?.setValue(`${c}${value}`); } public async loadData(): Promise { - const getData = (): - Promise => { - return (this.service as AdminService).getPrivacyPolicy(); + const getData = (): Promise => { + return this.service.getPrivacyPolicy(); }; - getData().then(resp => { - if (resp.policy) { - this.privacyPolicy = resp.policy; - this.form.patchValue(this.privacyPolicy); - } - }); + getData() + .then((resp) => { + if (resp.policy) { + this.privacyPolicy = resp.policy; + this.form.patchValue(this.privacyPolicy); + } else { + this.privacyPolicy = undefined; + this.form.patchValue({ + tosLink: '', + privacyLink: '', + helpLink: '', + }); + } + }) + .catch((error) => { + this.privacyPolicy = undefined; + this.form.patchValue({ + tosLink: '', + privacyLink: '', + helpLink: '', + }); + }); } public saveCurrentMessage(): void { - console.log(this.form.get('privacyLink')?.value, this.form.get('tosLink')?.value); if (this.serviceType === PolicyComponentServiceType.MGMT) { - if ((this.privacyPolicy as PrivacyPolicy.AsObject).isDefault) { + if (!this.privacyPolicy || (this.privacyPolicy as PrivacyPolicy.AsObject).isDefault) { const req = new AddCustomPrivacyPolicyRequest(); req.setPrivacyLink(this.form.get('privacyLink')?.value); req.setTosLink(this.form.get('tosLink')?.value); - (this.service as ManagementService).addCustomPrivacyPolicy(req).then(() => { - this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true); - }).catch(error => this.toast.showError(error)); + req.setHelpLink(this.form.get('helpLink')?.value); + (this.service as ManagementService) + .addCustomPrivacyPolicy(req) + .then(() => { + this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true); + this.loadData(); + }) + .catch((error) => this.toast.showError(error)); } else { const req = new UpdateCustomPrivacyPolicyRequest(); req.setPrivacyLink(this.form.get('privacyLink')?.value); req.setTosLink(this.form.get('tosLink')?.value); - (this.service as ManagementService).updateCustomPrivacyPolicy(req).then(() => { - this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true); - }).catch(error => this.toast.showError(error)); - } + req.setHelpLink(this.form.get('helpLink')?.value); + (this.service as ManagementService) + .updateCustomPrivacyPolicy(req) + .then(() => { + this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true); + this.loadData(); + }) + .catch((error) => this.toast.showError(error)); + } } else if (this.serviceType === PolicyComponentServiceType.ADMIN) { const req = new UpdatePrivacyPolicyRequest(); req.setPrivacyLink(this.form.get('privacyLink')?.value); req.setTosLink(this.form.get('tosLink')?.value); + req.setHelpLink(this.form.get('helpLink')?.value); - (this.service as AdminService).updatePrivacyPolicy(req).then(() => { - this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true); - }).catch(error => this.toast.showError(error)); + (this.service as AdminService) + .updatePrivacyPolicy(req) + .then(() => { + this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true); + this.loadData(); + }) + .catch((error) => this.toast.showError(error)); } } @@ -128,16 +169,19 @@ export class PrivacyPolicyComponent implements OnDestroy { width: '400px', }); - dialogRef.afterClosed().subscribe(resp => { + dialogRef.afterClosed().subscribe((resp) => { if (resp) { if (this.serviceType === PolicyComponentServiceType.MGMT) { - (this.service as ManagementService).resetPrivacyPolicyToDefault().then(() => { - setTimeout(() => { - this.loadData(); - }, 1000); - }).catch(error => { - this.toast.showError(error); - }); + (this.service as ManagementService) + .resetPrivacyPolicyToDefault() + .then(() => { + setTimeout(() => { + this.loadData(); + }, 1000); + }) + .catch((error) => { + this.toast.showError(error); + }); } } }); diff --git a/console/src/app/modules/policies/privacy-policy/privacy-policy.module.ts b/console/src/app/modules/policies/privacy-policy/privacy-policy.module.ts index 201208f026..ff6f9b2419 100644 --- a/console/src/app/modules/policies/privacy-policy/privacy-policy.module.ts +++ b/console/src/app/modules/policies/privacy-policy/privacy-policy.module.ts @@ -9,6 +9,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; 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 '../../../directives/has-role/has-role.module'; import { DetailLayoutModule } from '../../../modules/detail-layout/detail-layout.module'; @@ -32,6 +33,7 @@ import { PrivacyPolicyComponent } from './privacy-policy.component'; FormsModule, InputModule, FormFieldModule, + CopyToClipboardModule, MatButtonModule, HasFeaturePipeModule, MatIconModule, @@ -49,4 +51,4 @@ import { PrivacyPolicyComponent } from './privacy-policy.component'; InfoSectionModule, ], }) -export class PrivacyPolicyModule { } +export class PrivacyPolicyModule {} diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index c751dc34a5..69e2a38305 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -873,6 +873,7 @@ "DESCRIPTION": "Legen Sie Ihre Datenschutzrichtlinien und Nutzungsbedingungen fest", "TOSLINK": "Link zu den Allgemeinen Geschäftsbedingungen", "POLICYLINK": "Link zur den Datenschutzrichtlinien", + "HELPLINK": "Link zur Hilfestellung", "SAVED": "Saved successfully!", "RESET_TITLE": "Standardwerte wiederherstellen", "RESET_DESCRIPTION": "Sie sind im Begriff die Standardlinks für die AGBs und Datenschutzrichtlinie wiederherzustellen. Wollen Sie fortfahren?" diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 4aad40dfc6..646f68abde 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -873,6 +873,7 @@ "DESCRIPTION": "Set your Privacy Policy and Terms of Service Links", "TOSLINK": "Link to Terms of Service", "POLICYLINK": "Link to Privacy Policy", + "HELPLINK": "Link to Help", "SAVED": "Saved successfully!", "RESET_TITLE": "Restore Default Values", "RESET_DESCRIPTION": "You are about to restore the default Links for TOS and Privacy Policy. Do you really want to continue?" diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 74738375a1..4f73d67c53 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -873,6 +873,7 @@ "DESCRIPTION": "Imposta i tuoi link all'informativa sulla privacy e ai termini di servizio", "TOSLINK": "Link ai termini di servizio", "POLICYLINK": "Link all'informativa sulla privacy", + "HELPLINK": "link per l'aiuto", "SAVED": "Salvato con successo!", "RESET_TITLE": "Ripristina i valori predefiniti", "RESET_DESCRIPTION": "Stai per ripristinare i link predefiniti per i TOS e l'informativa sulla privacy. Vuoi davvero continuare?" diff --git a/docs/docs/apis/proto/admin.md b/docs/docs/apis/proto/admin.md index 5c28614c35..37f9be8270 100644 --- a/docs/docs/apis/proto/admin.md +++ b/docs/docs/apis/proto/admin.md @@ -888,6 +888,7 @@ Returns the privacy policy defined by the administrators of ZITADEL Updates the default privacy policy of ZITADEL it impacts all organisations without a customised policy +Variable {{.Lang}} can be set to have different links based on the language @@ -3816,6 +3817,7 @@ This is an empty request | ----- | ---- | ----------- | ----------- | | tos_link | string | - | | | privacy_link | string | - | | +| help_link | string | - | | diff --git a/docs/docs/apis/proto/management.md b/docs/docs/apis/proto/management.md index bd5484afe6..100929d241 100644 --- a/docs/docs/apis/proto/management.md +++ b/docs/docs/apis/proto/management.md @@ -2095,6 +2095,7 @@ With this policy the privacy relevant things can be configured (e.g tos link) Add a custom privacy policy for the organisation With this policy privacy relevant things can be configured (e.g. tos link) +Variable {{.Lang}} can be set to have different links based on the language @@ -2108,6 +2109,7 @@ With this policy privacy relevant things can be configured (e.g. tos link) Update the privacy complexity policy for the organisation With this policy privacy relevant things can be configured (e.g. tos link) +Variable {{.Lang}} can be set to have different links based on the language @@ -3113,6 +3115,7 @@ This is an empty request | ----- | ---- | ----------- | ----------- | | tos_link | string | - | | | privacy_link | string | - | | +| help_link | string | - | | @@ -7833,6 +7836,7 @@ This is an empty request | ----- | ---- | ----------- | ----------- | | tos_link | string | - | | | privacy_link | string | - | | +| help_link | string | - | | diff --git a/docs/docs/apis/proto/policy.md b/docs/docs/apis/proto/policy.md index cd482b7ff6..0820f88c95 100644 --- a/docs/docs/apis/proto/policy.md +++ b/docs/docs/apis/proto/policy.md @@ -126,6 +126,7 @@ title: zitadel/policy.proto | tos_link | string | - | | | privacy_link | string | - | | | is_default | bool | - | | +| help_link | string | - | | diff --git a/docs/docs/apis/proto/text.md b/docs/docs/apis/proto/text.md index dabf6946f7..cfd74d04aa 100644 --- a/docs/docs/apis/proto/text.md +++ b/docs/docs/apis/proto/text.md @@ -93,7 +93,6 @@ title: zitadel/text.proto | tos | string | - | string.max_len: 200
| | privacy_policy | string | - | string.max_len: 200
| | help | string | - | string.max_len: 200
| -| help_link | string | - | string.max_len: 500
| diff --git a/docs/docs/apis/proto/user.md b/docs/docs/apis/proto/user.md index 2f47a39135..6143aab915 100644 --- a/docs/docs/apis/proto/user.md +++ b/docs/docs/apis/proto/user.md @@ -374,6 +374,7 @@ UserTypeQuery is always equals | project_name | string | - | | | project_grant_id | string | - | | | avatar_url | string | - | | +| preferred_login_name | string | - | | diff --git a/go.mod b/go.mod index 40b4e7d838..31ce1b3101 100644 --- a/go.mod +++ b/go.mod @@ -44,10 +44,12 @@ require ( github.com/pquerna/otp v1.3.0 github.com/rakyll/statik v0.1.7 github.com/rs/cors v1.8.0 + github.com/sirupsen/logrus v1.8.1 github.com/sony/sonyflake v1.0.0 github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.10.1 github.com/stretchr/testify v1.7.0 + github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 github.com/ttacon/libphonenumber v1.2.1 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.27.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 @@ -88,17 +90,28 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect + github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc // indirect + github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4 // indirect + github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb // indirect + github.com/dsoprea/go-jpeg-image-structure v0.0.0-20210128210355-86b1014917f2 // indirect + github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d // indirect + github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c // indirect + github.com/dsoprea/go-png-image-structure v0.0.0-20200807080309-a98d4e94ac82 // indirect + github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/envoyproxy/go-control-plane v0.10.1 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/fxamacker/cbor/v2 v2.2.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-errors/errors v1.0.2 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect + github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect github.com/golang-jwt/jwt/v4 v4.1.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/certificate-transparency-go v1.0.21 // indirect @@ -107,6 +120,7 @@ require ( github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect + github.com/h2non/filetype v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect @@ -139,7 +153,6 @@ require ( github.com/prometheus/procfs v0.6.0 // indirect github.com/rs/xid v1.2.1 // indirect github.com/satori/go.uuid v1.2.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/afero v1.8.1 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index 13c1281d47..393b7c4223 100644 --- a/go.sum +++ b/go.sum @@ -203,6 +203,24 @@ github.com/dop251/goja v0.0.0-20211129110639-4739a1d10a51/go.mod h1:R9ET47fwRVRP github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d h1:W1n4DvpzZGOISgp7wWNtraLcHtnmnTwBlJidqtMIuwQ= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc h1:AuzYp98IFVOi0NU/WcZyGDQ6vAh/zkCjxGD3kt8aLzA= +github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs= +github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= +github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4 h1:Mg7pY7kxDQD2Bkvr1N+XW4BESSIQ7tTTR7Vv+Gi2CsM= +github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0= +github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4= +github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM= +github.com/dsoprea/go-jpeg-image-structure v0.0.0-20210128210355-86b1014917f2 h1:ULCSN6v0WISNbALxomGPXh4dSjRKPW+7+seYoMz8UTc= +github.com/dsoprea/go-jpeg-image-structure v0.0.0-20210128210355-86b1014917f2/go.mod h1:ZoOP3yUG0HD1T4IUjIFsz/2OAB2yB4YX6NSm4K+uJRg= +github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA= +github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d h1:F/7L5wr/fP/SKeO5HuMlNEX9Ipyx2MbH2rV9G4zJRpk= +github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8= +github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c h1:7j5aWACOzROpr+dvMtu8GnI97g9ShLWD72XIELMgn+c= +github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c/go.mod h1:pqKB+ijp27cEcrHxhXVgUUMlSDRuGJJp1E+20Lj5H0E= +github.com/dsoprea/go-png-image-structure v0.0.0-20200807080309-a98d4e94ac82 h1:RdwKOEEe2ND/JmoKh6I/EQlR9idKJTDOMffPFK6vN2M= +github.com/dsoprea/go-png-image-structure v0.0.0-20200807080309-a98d4e94ac82/go.mod h1:aDYQkL/5gfRNZkoxiLTSWU4Y8/gV/4MVsy/MU9uwTak= +github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 h1:CfXezFYb2STGOd1+n1HshvE191zVx+QX3A1nML5xxME= +github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8= github.com/duo-labs/webauthn v0.0.0-20211216225436-9a12cd078b8a h1:mKoV2b/J8sVVvc6jCl7SxdOrED5cHKdQaHUxjoO5W74= github.com/duo-labs/webauthn v0.0.0-20211216225436-9a12cd078b8a/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -256,8 +274,9 @@ github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.0.2 h1:xMxH9j2fNg/L4hLn/4y3M0IUsn0M6Wbu/Uh9QlOfBh4= +github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -286,6 +305,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG 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/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= +github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= @@ -309,6 +330,9 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc= +github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= @@ -434,6 +458,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.1 h1:p5m7GOEGXyoq6QWl4/RRMsQ6tWbTpbQmAnkxXgWSprY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.1/go.mod h1:8ZeZajTed/blCOHBbj8Fss8bPHiFKcmJJzuIbUtFCAo= +github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4= +github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= @@ -535,6 +561,7 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -870,6 +897,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 h1:1SWXcTphBQjYGWRRxLFIAR1LVtQEj4eR7xPtyeOVM/c= +github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203/go.mod h1:0Xw5cYMOYpgaWs+OOSx41ugycl2qvKTi9tlMMcZhFyY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w= @@ -1073,6 +1102,7 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= diff --git a/internal/api/assets/asset.go b/internal/api/assets/asset.go index 6ca3298c22..374b5f6fd0 100644 --- a/internal/api/assets/asset.go +++ b/internal/api/assets/asset.go @@ -1,8 +1,10 @@ package assets import ( + "bytes" "context" "fmt" + "io" "io/ioutil" "net/http" "strconv" @@ -11,6 +13,7 @@ import ( "github.com/caos/logging" sentryhttp "github.com/getsentry/sentry-go/http" "github.com/gorilla/mux" + "github.com/superseriousbusiness/exifremove/pkg/exifremove" "github.com/caos/zitadel/internal/api/authz" http_mw "github.com/caos/zitadel/internal/api/http/middleware" @@ -136,7 +139,13 @@ func UploadHandleFunc(s AssetsService, uploader Uploader) func(http.ResponseWrit s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError) return } - info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, file, size) + cleanedFile, cleanedSize, err := removeExif(file, size, contentType) + if err != nil { + s.ErrorHandler()(w, r, fmt.Errorf("remove exif error: %v", err), http.StatusInternalServerError) + return + } + + info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, cleanedFile, cleanedSize) if err != nil { s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError) return @@ -191,3 +200,25 @@ func DownloadHandleFunc(s AssetsService, downloader Downloader) func(http.Respon w.Write(data) } } + +func removeExif(file io.Reader, size int64, contentType string) (io.Reader, int64, error) { + if !isAllowedContentType(contentType) { + return file, size, nil + } + buf := new(bytes.Buffer) + _, err := buf.ReadFrom(file) + if err != nil { + return file, 0, err + } + data, err := exifremove.Remove(buf.Bytes()) + if err != nil { + return nil, 0, err + } + return bytes.NewReader(data), int64(len(data)), nil +} + +func isAllowedContentType(contentType string) bool { + return strings.HasSuffix(contentType, "png") || + strings.HasSuffix(contentType, "jpg") || + strings.HasSuffix(contentType, "jpeg") +} diff --git a/internal/api/grpc/admin/privacy_policy_converter.go b/internal/api/grpc/admin/privacy_policy_converter.go index 2c2c9fb4cc..b21ea5451d 100644 --- a/internal/api/grpc/admin/privacy_policy_converter.go +++ b/internal/api/grpc/admin/privacy_policy_converter.go @@ -9,5 +9,6 @@ func UpdatePrivacyPolicyToDomain(req *admin_pb.UpdatePrivacyPolicyRequest) *doma return &domain.PrivacyPolicy{ TOSLink: req.TosLink, PrivacyLink: req.PrivacyLink, + HelpLink: req.HelpLink, } } diff --git a/internal/api/grpc/auth/permission.go b/internal/api/grpc/auth/permission.go index ea9ea464b2..41d771e018 100644 --- a/internal/api/grpc/auth/permission.go +++ b/internal/api/grpc/auth/permission.go @@ -11,7 +11,7 @@ import ( ) func (s *Server) ListMyZitadelPermissions(ctx context.Context, _ *auth_pb.ListMyZitadelPermissionsRequest) (*auth_pb.ListMyZitadelPermissionsResponse, error) { - perms, err := s.query.MyZitadelPermissions(ctx, authz.GetCtxData(ctx).UserID) + perms, err := s.query.MyZitadelPermissions(ctx, authz.GetCtxData(ctx).OrgID, authz.GetCtxData(ctx).UserID) if err != nil { return nil, err } diff --git a/internal/api/grpc/auth/user.go b/internal/api/grpc/auth/user.go index 067d2092fe..9c5bc7230b 100644 --- a/internal/api/grpc/auth/user.go +++ b/internal/api/grpc/auth/user.go @@ -168,6 +168,7 @@ func (s *Server) ListMyProjectOrgs(ctx context.Context, req *auth_pb.ListMyProje if err != nil { return nil, err } + grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantProjectID, userGrantUserID}}) if err != nil { return nil, err diff --git a/internal/api/grpc/management/policy_privacy_converter.go b/internal/api/grpc/management/policy_privacy_converter.go index f78c5ac12d..8971815626 100644 --- a/internal/api/grpc/management/policy_privacy_converter.go +++ b/internal/api/grpc/management/policy_privacy_converter.go @@ -9,6 +9,7 @@ func AddPrivacyPolicyToDomain(req *mgmt_pb.AddCustomPrivacyPolicyRequest) *domai return &domain.PrivacyPolicy{ TOSLink: req.TosLink, PrivacyLink: req.PrivacyLink, + HelpLink: req.HelpLink, } } @@ -16,5 +17,6 @@ func UpdatePrivacyPolicyToDomain(req *mgmt_pb.UpdateCustomPrivacyPolicyRequest) return &domain.PrivacyPolicy{ TOSLink: req.TosLink, PrivacyLink: req.PrivacyLink, + HelpLink: req.HelpLink, } } diff --git a/internal/api/grpc/management/project_grant_converter.go b/internal/api/grpc/management/project_grant_converter.go index 6c98c09dd4..966412f0fb 100644 --- a/internal/api/grpc/management/project_grant_converter.go +++ b/internal/api/grpc/management/project_grant_converter.go @@ -126,11 +126,6 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis if err != nil { return nil, err } - ownerQuery, err := query.NewMemberResourceOwnerSearchQuery(authz.GetCtxData(ctx).OrgID) - if err != nil { - return nil, err - } - queries = append(queries, ownerQuery) return &query.ProjectGrantMembersQuery{ MembersQuery: query.MembersQuery{ SearchRequest: query.SearchRequest{ @@ -143,6 +138,7 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis }, ProjectID: req.ProjectId, GrantID: req.GrantId, + OrgID: authz.GetCtxData(ctx).OrgID, }, nil } diff --git a/internal/api/grpc/org/converter.go b/internal/api/grpc/org/converter.go index 587da8c8e8..dfd592c649 100644 --- a/internal/api/grpc/org/converter.go +++ b/internal/api/grpc/org/converter.go @@ -88,7 +88,7 @@ func OrgToPb(org *query.Org) *org_pb.Org { Id: org.ID, Name: org.Name, PrimaryDomain: org.Domain, - Details: object.AddToDetailsPb(org.Sequence, org.CreationDate, org.ResourceOwner), + Details: object.ToViewDetailsPb(org.Sequence, org.CreationDate, org.ChangeDate, org.ResourceOwner), State: OrgStateToPb(org.State), } } diff --git a/internal/api/grpc/policy/privacy_policy.go b/internal/api/grpc/policy/privacy_policy.go index 6145668982..3913aa137c 100644 --- a/internal/api/grpc/policy/privacy_policy.go +++ b/internal/api/grpc/policy/privacy_policy.go @@ -11,6 +11,7 @@ func ModelPrivacyPolicyToPb(policy *query.PrivacyPolicy) *policy_pb.PrivacyPolic IsDefault: policy.IsDefault, TosLink: policy.TOSLink, PrivacyLink: policy.PrivacyLink, + HelpLink: policy.HelpLink, Details: object.ToViewDetailsPb( policy.Sequence, policy.CreationDate, diff --git a/internal/api/grpc/text/custom_text.go b/internal/api/grpc/text/custom_text.go index a3675345d8..5f4a1040a1 100644 --- a/internal/api/grpc/text/custom_text.go +++ b/internal/api/grpc/text/custom_text.go @@ -457,7 +457,6 @@ func FooterTextToPb(text domain.FooterText) *text_pb.FooterText { Tos: text.TOS, PrivacyPolicy: text.PrivacyPolicy, Help: text.Help, - HelpLink: text.HelpLink, } } @@ -947,6 +946,5 @@ func FooterTextPbToDomain(text *text_pb.FooterText) domain.FooterText { TOS: text.Tos, PrivacyPolicy: text.PrivacyPolicy, Help: text.Help, - HelpLink: text.HelpLink, } } diff --git a/internal/api/grpc/user/user_grant.go b/internal/api/grpc/user/user_grant.go index bdb074398d..6ccf8db646 100644 --- a/internal/api/grpc/user/user_grant.go +++ b/internal/api/grpc/user/user_grant.go @@ -21,22 +21,23 @@ func UserGrantsToPb(assetPrefix string, grants []*query.UserGrant) []*user_pb.Us func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGrant { return &user_pb.UserGrant{ - Id: grant.ID, - UserId: grant.UserID, - State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE, - RoleKeys: grant.Roles, - ProjectId: grant.ProjectID, - OrgId: grant.ResourceOwner, - ProjectGrantId: grant.GrantID, - UserName: grant.Username, - FirstName: grant.FirstName, - LastName: grant.LastName, - Email: grant.Email, - DisplayName: grant.DisplayName, - OrgDomain: grant.OrgPrimaryDomain, - OrgName: grant.OrgName, - ProjectName: grant.ProjectName, - AvatarUrl: domain.AvatarURL(assetPrefix, grant.UserResourceOwner, grant.AvatarURL), + Id: grant.ID, + UserId: grant.UserID, + State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE, + RoleKeys: grant.Roles, + ProjectId: grant.ProjectID, + OrgId: grant.ResourceOwner, + ProjectGrantId: grant.GrantID, + UserName: grant.Username, + FirstName: grant.FirstName, + LastName: grant.LastName, + Email: grant.Email, + DisplayName: grant.DisplayName, + OrgDomain: grant.OrgPrimaryDomain, + OrgName: grant.OrgName, + ProjectName: grant.ProjectName, + AvatarUrl: domain.AvatarURL(assetPrefix, grant.UserResourceOwner, grant.AvatarURL), + PreferredLoginName: grant.PreferredLoginName, Details: object.ToViewDetailsPb( grant.Sequence, grant.CreationDate, diff --git a/internal/api/oidc/auth_request.go b/internal/api/oidc/auth_request.go index 79460f19a6..5bde39d1ee 100644 --- a/internal/api/oidc/auth_request.go +++ b/internal/api/oidc/auth_request.go @@ -95,7 +95,7 @@ func (o *OPStorage) CreateAccessToken(ctx context.Context, req op.TokenRequest) applicationID = authReq.ApplicationID userOrgID = authReq.UserOrgID } - resp, err := o.command.AddUserToken(ctx, userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client + resp, err := o.command.AddUserToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client if err != nil { return "", time.Time{}, err } @@ -123,7 +123,7 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok if request, ok := req.(op.RefreshTokenRequest); ok { request.SetCurrentScopes(scopes) } - resp, token, err := o.command.AddAccessAndRefreshToken(ctx, userOrgID, userAgentID, applicationID, req.GetSubject(), + resp, token, err := o.command.AddAccessAndRefreshToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), refreshToken, req.GetAudience(), scopes, authMethodsReferences, o.defaultAccessTokenLifetime, o.defaultRefreshTokenIdleExpiration, o.defaultRefreshTokenExpiration, authTime) //PLANNED: lifetime from client if err != nil { @@ -171,7 +171,10 @@ func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID strin if len(userIDs) == 0 { return nil } - err = o.command.HumansSignOut(ctx, userAgentID, userIDs) + data := authz.CtxData{ + UserID: userID, + } + err = o.command.HumansSignOut(authz.SetCtxData(ctx, data), userAgentID, userIDs) logging.Log("OIDC-Dggt2").OnError(err).Error("error signing out") return err } @@ -255,3 +258,10 @@ func (o *OPStorage) assertClientScopesForPAT(ctx context.Context, token *model.T } return nil } + +func setContextUserSystem(ctx context.Context) context.Context { + data := authz.CtxData{ + UserID: "SYSTEM", + } + return authz.SetCtxData(ctx, data) +} diff --git a/internal/api/ui/login/external_login_handler.go b/internal/api/ui/login/external_login_handler.go index 166bc8474c..fd12f6ee70 100644 --- a/internal/api/ui/login/external_login_handler.go +++ b/internal/api/ui/login/external_login_handler.go @@ -203,7 +203,7 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R } instanceID := authz.GetInstance(r.Context()).ID - err = l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, userAgentID, instanceID, externalUser, domain.BrowserInfoFromRequest(r)) + err = l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, userAgentID, instanceID, externalUser, domain.BrowserInfoFromRequest(r)) if err != nil { if errors.IsNotFound(err) { err = nil diff --git a/internal/api/ui/login/jwt_handler.go b/internal/api/ui/login/jwt_handler.go index 55b2471440..321d66b086 100644 --- a/internal/api/ui/login/jwt_handler.go +++ b/internal/api/ui/login/jwt_handler.go @@ -84,7 +84,7 @@ func (l *Login) handleJWTExtraction(w http.ResponseWriter, r *http.Request, auth return } metadata := externalUser.Metadatas - err = l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, authReq.AgentID, authReq.InstanceID, externalUser, domain.BrowserInfoFromRequest(r)) + err = l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, authReq.AgentID, authReq.InstanceID, externalUser, domain.BrowserInfoFromRequest(r)) if err != nil { l.jwtExtractionUserNotFound(w, r, authReq, idpConfig, tokens, err) return diff --git a/internal/api/ui/login/renderer.go b/internal/api/ui/login/renderer.go index 4e8901cf37..42c76d8e65 100644 --- a/internal/api/ui/login/renderer.go +++ b/internal/api/ui/login/renderer.go @@ -16,6 +16,7 @@ import ( "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/i18n" + "github.com/caos/zitadel/internal/notification/templates" "github.com/caos/zitadel/internal/renderer" "github.com/caos/zitadel/internal/static" ) @@ -30,6 +31,10 @@ type Renderer struct { staticStorage static.Storage } +type LanguageData struct { + Lang string +} + func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage static.Storage, cookieName string, defaultLanguage language.Tag) *Renderer { r := &Renderer{ pathPrefix: pathPrefix, @@ -345,24 +350,23 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title CSRF: csrf.TemplateField(r), Nonce: http_mw.GetNonce(r), } + var privacyPolicy *domain.PrivacyPolicy if authReq != nil { baseData.LoginPolicy = authReq.LoginPolicy baseData.LabelPolicy = authReq.LabelPolicy baseData.IDPProviders = authReq.AllowedExternalIDPs - if authReq.PrivacyPolicy != nil { - baseData.TOSLink = authReq.PrivacyPolicy.TOSLink - baseData.PrivacyLink = authReq.PrivacyPolicy.PrivacyLink + if authReq.PrivacyPolicy == nil { + return baseData } + privacyPolicy = authReq.PrivacyPolicy } else { - privacyPolicy, err := l.query.DefaultPrivacyPolicy(r.Context()) + policy, err := l.query.DefaultPrivacyPolicy(r.Context()) if err != nil { return baseData } - if privacyPolicy != nil { - baseData.TOSLink = privacyPolicy.TOSLink - baseData.PrivacyLink = privacyPolicy.PrivacyLink - } + privacyPolicy = policy.ToDomain() } + baseData = l.setLinksOnBaseData(baseData, privacyPolicy) return baseData } @@ -392,6 +396,26 @@ func (l *Login) getProfileData(authReq *domain.AuthRequest) profileData { } } +func (l *Login) setLinksOnBaseData(baseData baseData, privacyPolicy *domain.PrivacyPolicy) baseData { + lang := LanguageData{ + Lang: baseData.Lang, + } + baseData.TOSLink = privacyPolicy.TOSLink + baseData.PrivacyLink = privacyPolicy.PrivacyLink + baseData.HelpLink = privacyPolicy.HelpLink + + if link, err := templates.ParseTemplateText(privacyPolicy.TOSLink, lang); err == nil { + baseData.TOSLink = link + } + if link, err := templates.ParseTemplateText(privacyPolicy.PrivacyLink, lang); err == nil { + baseData.PrivacyLink = link + } + if link, err := templates.ParseTemplateText(privacyPolicy.HelpLink, lang); err == nil { + baseData.HelpLink = link + } + return baseData +} + func (l *Login) getErrorMessage(r *http.Request, err error) (errID, errMsg string) { caosErr := new(caos_errs.CaosError) if errors.As(err, &caosErr) { @@ -519,6 +543,7 @@ type baseData struct { DisplayLoginNameSuffix bool TOSLink string PrivacyLink string + HelpLink string AuthReqID string CSRF template.HTML Nonce string diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index 281a09288b..c4758fb660 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -297,7 +297,6 @@ Footer: Tos: AGB PrivacyPolicy: Datenschutzerklärung Help: Hilfe - HelpLink: https://docs.zitadel.ch/docs/manuals/user-login Errors: Internal: Es ist ein interner Fehler aufgetreten diff --git a/internal/api/ui/login/static/i18n/en.yaml b/internal/api/ui/login/static/i18n/en.yaml index 9e9d01b5c5..8fb2f6678f 100644 --- a/internal/api/ui/login/static/i18n/en.yaml +++ b/internal/api/ui/login/static/i18n/en.yaml @@ -298,7 +298,6 @@ Footer: Tos: TOS PrivacyPolicy: Privacy policy Help: Help - HelpLink: https://docs.zitadel.ch/docs/manuals/user-login Errors: Internal: An internal error occured diff --git a/internal/api/ui/login/static/i18n/it.yaml b/internal/api/ui/login/static/i18n/it.yaml index b6a4784224..ec6b7254c4 100644 --- a/internal/api/ui/login/static/i18n/it.yaml +++ b/internal/api/ui/login/static/i18n/it.yaml @@ -298,7 +298,6 @@ Footer: Tos: Termini di servizio PrivacyPolicy: l'informativa sulla privacy Help: Aiuto - HelpLink: 'https://docs.zitadel.ch/docs/manuals/user-login' Errors: Internal: Si è verificato un errore interno diff --git a/internal/api/ui/login/static/templates/footer.html b/internal/api/ui/login/static/templates/footer.html index 98965443f1..e4f7578b51 100644 --- a/internal/api/ui/login/static/templates/footer.html +++ b/internal/api/ui/login/static/templates/footer.html @@ -13,6 +13,8 @@ {{ if .PrivacyLink }} {{t "Footer.PrivacyPolicy"}} {{end}} - {{t "Footer.Help"}} + {{ if .HelpLink }} + {{t "Footer.Help"}} + {{end}} {{end}} diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index f3326a4567..085154b503 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -560,7 +560,7 @@ func (repo *AuthRequestRepo) fillPolicies(ctx context.Context, request *domain.A return err } request.LockoutPolicy = lockoutPolicyToDomain(lockoutPolicy) - privacyPolicy, err := repo.getPrivacyPolicy(ctx, orgID) + privacyPolicy, err := repo.GetPrivacyPolicy(ctx, orgID) if err != nil { return err } @@ -936,8 +936,11 @@ func (repo *AuthRequestRepo) mfaSkippedOrSetUp(user *user_model.UserView, reques return checkVerificationTime(user.MFAInitSkipped, request.LoginPolicy.MFAInitSkipLifetime) } -func (repo *AuthRequestRepo) getPrivacyPolicy(ctx context.Context, orgID string) (*domain.PrivacyPolicy, error) { +func (repo *AuthRequestRepo) GetPrivacyPolicy(ctx context.Context, orgID string) (*domain.PrivacyPolicy, error) { policy, err := repo.PrivacyPolicyProvider.PrivacyPolicyByOrg(ctx, orgID) + if errors.IsNotFound(err) { + return new(domain.PrivacyPolicy), nil + } if err != nil { return nil, err } @@ -957,6 +960,7 @@ func privacyPolicyToDomain(p *query.PrivacyPolicy) *domain.PrivacyPolicy { Default: p.IsDefault, TOSLink: p.TOSLink, PrivacyLink: p.PrivacyLink, + HelpLink: p.HelpLink, } } diff --git a/internal/auth/repository/eventsourcing/handler/user.go b/internal/auth/repository/eventsourcing/handler/user.go index 8463777200..9fa3203d52 100644 --- a/internal/auth/repository/eventsourcing/handler/user.go +++ b/internal/auth/repository/eventsourcing/handler/user.go @@ -4,8 +4,9 @@ import ( "context" "github.com/caos/logging" + "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1" + v1 "github.com/caos/zitadel/internal/eventstore/v1" es_models "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/query" es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk" @@ -172,19 +173,12 @@ func (u *User) ProcessUser(event *es_models.Event) (err error) { } func (u *User) fillLoginNames(user *view_model.UserView) (err error) { - org, err := u.getOrgByID(context.Background(), user.ResourceOwner) + userLoginMustBeDomain, primaryDomain, domains, err := u.loginNameInformation(context.Background(), user.ResourceOwner) if err != nil { return err } - policy := new(query2.OrgIAMPolicy) - if policy == nil { - policy, err = u.getDefaultOrgIAMPolicy(context.Background()) - if err != nil { - return err - } - } - user.SetLoginNames(policy, org.Domains) - user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + user.SetLoginNames(userLoginMustBeDomain, domains) + user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain) return nil } @@ -204,40 +198,26 @@ func (u *User) ProcessOrg(event *es_models.Event) (err error) { } func (u *User) fillLoginNamesOnOrgUsers(event *es_models.Event) error { - org, err := u.getOrgByID(context.Background(), event.ResourceOwner) + userLoginMustBeDomain, _, domains, err := u.loginNameInformation(context.Background(), event.ResourceOwner) if err != nil { return err } - policy := new(query2.OrgIAMPolicy) - if policy == nil { - policy, err = u.getDefaultOrgIAMPolicy(context.Background()) - if err != nil { - return err - } - } users, err := u.view.UsersByOrgID(event.AggregateID) if err != nil { return err } for _, user := range users { - user.SetLoginNames(policy, org.Domains) + user.SetLoginNames(userLoginMustBeDomain, domains) } return u.view.PutUsers(users, event) } func (u *User) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error { - org, err := u.getOrgByID(context.Background(), event.ResourceOwner) + userLoginMustBeDomain, primaryDomain, _, err := u.loginNameInformation(context.Background(), event.ResourceOwner) if err != nil { return err } - policy := new(query2.OrgIAMPolicy) - if policy == nil { - policy, err = u.getDefaultOrgIAMPolicy(context.Background()) - if err != nil { - return err - } - } - if !policy.UserLoginMustBeDomain { + if !userLoginMustBeDomain { return nil } users, err := u.view.UsersByOrgID(event.AggregateID) @@ -245,7 +225,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error { return err } for _, user := range users { - user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain) } return u.view.PutUsers(users, event) } @@ -281,6 +261,17 @@ func (u *User) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, er return org_es_model.OrgToModel(esOrg), nil } -func (u *User) getDefaultOrgIAMPolicy(ctx context.Context) (*query2.OrgIAMPolicy, error) { - return u.queries.DefaultOrgIAMPolicy(ctx) +func (u *User) loginNameInformation(ctx context.Context, orgID string) (userLoginMustBeDomain bool, primaryDomain string, domains []*org_model.OrgDomain, err error) { + org, err := u.getOrgByID(ctx, orgID) + if err != nil { + return false, "", nil, err + } + if org.OrgIamPolicy == nil { + policy, err := u.queries.DefaultOrgIAMPolicy(ctx) + if err != nil { + return false, "", nil, err + } + userLoginMustBeDomain = policy.UserLoginMustBeDomain + } + return userLoginMustBeDomain, org.GetPrimaryDomain().Domain, org.Domains, nil } diff --git a/internal/command/custom_login_text.go b/internal/command/custom_login_text.go index 9ea20b55d7..bbf53dd31a 100644 --- a/internal/command/custom_login_text.go +++ b/internal/command/custom_login_text.go @@ -1089,10 +1089,6 @@ func (c *Commands) createFooterTextEvents(ctx context.Context, agg *eventstore.A if event != nil { events = append(events, event) } - event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterHelpLink, existingText.FooterHelpLink, text.Footer.HelpLink, text.Language, defaultText) - if event != nil { - events = append(events, event) - } return events } diff --git a/internal/command/custom_login_text_model.go b/internal/command/custom_login_text_model.go index f5b806ff1c..9d50ee4b6f 100644 --- a/internal/command/custom_login_text_model.go +++ b/internal/command/custom_login_text_model.go @@ -2508,10 +2508,6 @@ func (wm *CustomLoginTextReadModel) handleFooterTextSetEvent(e *policy.CustomTex wm.FooterHelp = e.Text return } - if e.Key == domain.LoginKeyFooterHelpLink { - wm.FooterHelpLink = e.Text - return - } } func (wm *CustomLoginTextReadModel) handleFooterTextRemoveEvent(e *policy.CustomTextRemovedEvent) { @@ -2527,8 +2523,4 @@ func (wm *CustomLoginTextReadModel) handleFooterTextRemoveEvent(e *policy.Custom wm.FooterHelp = "" return } - if e.Key == domain.LoginKeyFooterHelpLink { - wm.FooterHelpLink = "" - return - } } diff --git a/internal/command/iam_converter.go b/internal/command/iam_converter.go index e9bab1285e..ddc9e81bc5 100644 --- a/internal/command/iam_converter.go +++ b/internal/command/iam_converter.go @@ -130,6 +130,7 @@ func writeModelToPrivacyPolicy(wm *PrivacyPolicyWriteModel) *domain.PrivacyPolic ObjectRoot: writeModelToObjectRoot(wm.WriteModel), TOSLink: wm.TOSLink, PrivacyLink: wm.PrivacyLink, + HelpLink: wm.HelpLink, } } diff --git a/internal/command/iam_custom_login_text_test.go b/internal/command/iam_custom_login_text_test.go index f670c46696..b9b62e83fb 100644 --- a/internal/command/iam_custom_login_text_test.go +++ b/internal/command/iam_custom_login_text_test.go @@ -1143,11 +1143,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - iam.NewCustomTextSetEvent(context.Background(), - &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), }, ), ), @@ -1441,7 +1436,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { TOS: "TOS", PrivacyPolicy: "PrivacyPolicy", Help: "Help", - HelpLink: "HelpLink", }, }, }, @@ -2547,11 +2541,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - iam.NewCustomTextSetEvent(context.Background(), - &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), ), expectPush( []*repository.Event{ @@ -3645,11 +3634,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, ), ), - eventFromEventPusher( - iam.NewCustomTextRemovedEvent(context.Background(), - &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English, - ), - ), }, ), ), @@ -4797,11 +4781,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - iam.NewCustomTextSetEvent(context.Background(), - &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), eventFromEventPusher( iam.NewCustomTextRemovedEvent(context.Background(), &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English, @@ -5892,11 +5871,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, ), ), - eventFromEventPusher( - iam.NewCustomTextRemovedEvent(context.Background(), - &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English, - ), - ), ), expectPush( []*repository.Event{ @@ -6990,11 +6964,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - iam.NewCustomTextSetEvent(context.Background(), - &iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), }, ), ), @@ -7288,7 +7257,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) { TOS: "TOS", PrivacyPolicy: "PrivacyPolicy", Help: "Help", - HelpLink: "HelpLink", }, }, }, diff --git a/internal/command/iam_policy_privacy.go b/internal/command/iam_policy_privacy.go index 290662a3b1..6f428e65d5 100644 --- a/internal/command/iam_policy_privacy.go +++ b/internal/command/iam_policy_privacy.go @@ -52,7 +52,7 @@ func (c *Commands) addDefaultPrivacyPolicy(ctx context.Context, iamAgg *eventsto return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-M00rJ", "Errors.IAM.PrivacyPolicy.AlreadyExists") } - return iam_repo.NewPrivacyPolicyAddedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink), nil + return iam_repo.NewPrivacyPolicyAddedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink), nil } func (c *Commands) ChangeDefaultPrivacyPolicy(ctx context.Context, policy *domain.PrivacyPolicy) (*domain.PrivacyPolicy, error) { @@ -65,7 +65,7 @@ func (c *Commands) ChangeDefaultPrivacyPolicy(ctx context.Context, policy *domai } iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PrivacyPolicyWriteModel.WriteModel) - changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink) + changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink) if !hasChanged { return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged") } diff --git a/internal/command/iam_policy_privacy_model.go b/internal/command/iam_policy_privacy_model.go index ba5cc80d64..83b5aafb8e 100644 --- a/internal/command/iam_policy_privacy_model.go +++ b/internal/command/iam_policy_privacy_model.go @@ -56,7 +56,8 @@ func (wm *IAMPrivacyPolicyWriteModel) NewChangedEvent( ctx context.Context, aggregate *eventstore.Aggregate, tosLink, - privacyLink string, + privacyLink, + helpLink string, ) (*iam.PrivacyPolicyChangedEvent, bool) { changes := make([]policy.PrivacyPolicyChanges, 0) @@ -66,6 +67,9 @@ func (wm *IAMPrivacyPolicyWriteModel) NewChangedEvent( if wm.PrivacyLink != privacyLink { changes = append(changes, policy.ChangePrivacyLink(privacyLink)) } + if wm.HelpLink != helpLink { + changes = append(changes, policy.ChangeHelpLink(helpLink)) + } if len(changes) == 0 { return nil, false } diff --git a/internal/command/iam_policy_privacy_test.go b/internal/command/iam_policy_privacy_test.go index 8c0a2eaf5a..79ee3e2633 100644 --- a/internal/command/iam_policy_privacy_test.go +++ b/internal/command/iam_policy_privacy_test.go @@ -2,6 +2,10 @@ package command import ( "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore" @@ -9,8 +13,6 @@ import ( "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/repository/iam" "github.com/caos/zitadel/internal/repository/policy" - "github.com/stretchr/testify/assert" - "testing" ) func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { @@ -42,6 +44,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { &iam.NewAggregate().Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), @@ -52,6 +55,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -71,6 +75,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { &iam.NewAggregate().Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), }, @@ -82,6 +87,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -92,6 +98,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { }, TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, }, @@ -108,6 +115,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { &iam.NewAggregate().Aggregate, "", "", + "", ), ), }, @@ -119,6 +127,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "", PrivacyLink: "", + HelpLink: "", }, }, res: res{ @@ -129,6 +138,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { }, TOSLink: "", PrivacyLink: "", + HelpLink: "", }, }, }, @@ -183,6 +193,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -200,6 +211,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { &iam.NewAggregate().Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), @@ -210,6 +222,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -227,6 +240,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { &iam.NewAggregate().Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), @@ -236,6 +250,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { newDefaultPrivacyPolicyChangedEvent(context.Background(), "TOSLinkChanged", "PrivacyLinkChanged", + "HelpLinkChanged", ), ), }, @@ -247,6 +262,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLinkChanged", PrivacyLink: "PrivacyLinkChanged", + HelpLink: "HelpLinkChanged", }, }, res: res{ @@ -257,6 +273,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { }, TOSLink: "TOSLinkChanged", PrivacyLink: "PrivacyLinkChanged", + HelpLink: "HelpLinkChanged", }, }, }, @@ -280,12 +297,13 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) { } } -func newDefaultPrivacyPolicyChangedEvent(ctx context.Context, tosLink, privacyLink string) *iam.PrivacyPolicyChangedEvent { +func newDefaultPrivacyPolicyChangedEvent(ctx context.Context, tosLink, privacyLink, helpLink string) *iam.PrivacyPolicyChangedEvent { event, _ := iam.NewPrivacyPolicyChangedEvent(ctx, &iam.NewAggregate().Aggregate, []policy.PrivacyPolicyChanges{ policy.ChangeTOSLink(tosLink), policy.ChangePrivacyLink(privacyLink), + policy.ChangeHelpLink(helpLink), }, ) return event diff --git a/internal/command/org_custom_login_text_test.go b/internal/command/org_custom_login_text_test.go index d0bc837f43..40d28862b1 100644 --- a/internal/command/org_custom_login_text_test.go +++ b/internal/command/org_custom_login_text_test.go @@ -1161,11 +1161,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), }, ), ), @@ -1460,7 +1455,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { TOS: "TOS", PrivacyPolicy: "PrivacyPolicy", Help: "Help", - HelpLink: "HelpLink", }, }, }, @@ -2566,11 +2560,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), ), expectPush( []*repository.Event{ @@ -3664,11 +3653,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, ), ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English, - ), - ), }, ), ), @@ -4816,11 +4800,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), eventFromEventPusher( org.NewCustomTextRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English, @@ -5911,11 +5890,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English, ), ), - eventFromEventPusher( - org.NewCustomTextRemovedEvent(context.Background(), - &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English, - ), - ), ), expectPush( []*repository.Event{ @@ -7009,11 +6983,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English, ), ), - eventFromEventPusher( - org.NewCustomTextSetEvent(context.Background(), - &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English, - ), - ), }, ), ), @@ -7308,7 +7277,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) { TOS: "TOS", PrivacyPolicy: "PrivacyPolicy", Help: "Help", - HelpLink: "HelpLink", }, }, }, diff --git a/internal/command/org_features_test.go b/internal/command/org_features_test.go index 917fdfde52..0128d5fd46 100644 --- a/internal/command/org_features_test.go +++ b/internal/command/org_features_test.go @@ -5,12 +5,13 @@ import ( "testing" "time" - "github.com/caos/zitadel/internal/repository/user" - "github.com/caos/zitadel/internal/static/mock" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "golang.org/x/text/language" + "github.com/caos/zitadel/internal/repository/user" + "github.com/caos/zitadel/internal/static/mock" + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore" @@ -266,6 +267,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { &iam.NewAggregate().Aggregate, "toslink", "privacylink", + "helpLink", ), ), ), @@ -470,6 +472,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { &iam.NewAggregate().Aggregate, "toslink", "privacylink", + "helplink", ), ), ), @@ -686,6 +689,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { &iam.NewAggregate().Aggregate, "toslink", "privacylink", + "helplink", ), ), ), @@ -912,6 +916,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { &iam.NewAggregate().Aggregate, "toslink", "privacylink", + "helplink", ), ), ), @@ -1203,6 +1208,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { &iam.NewAggregate().Aggregate, "toslink", "privacylink", + "helplink", ), ), ), @@ -1420,6 +1426,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { &iam.NewAggregate().Aggregate, "toslink", "privacylink", + "helplink", ), ), ), @@ -1684,6 +1691,7 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) { &iam.NewAggregate().Aggregate, "toslink", "privacylink", + "helplink", ), ), ), diff --git a/internal/command/org_policy_org_iam.go b/internal/command/org_policy_org_iam.go index e327c0467a..8001e29cd0 100644 --- a/internal/command/org_policy_org_iam.go +++ b/internal/command/org_policy_org_iam.go @@ -80,7 +80,7 @@ func (c *Commands) RemoveOrgIAMPolicy(ctx context.Context, orgID string) error { return err } if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved { - return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.OrgIAM.NotFound") + return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.OrgIAMPolicy.NotFound") } orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel) diff --git a/internal/command/org_policy_privacy.go b/internal/command/org_policy_privacy.go index 9c45e2cc37..3dea4c36ad 100644 --- a/internal/command/org_policy_privacy.go +++ b/internal/command/org_policy_privacy.go @@ -48,7 +48,8 @@ func (c *Commands) AddPrivacyPolicy(ctx context.Context, resourceOwner string, p ctx, orgAgg, policy.TOSLink, - policy.PrivacyLink)) + policy.PrivacyLink, + policy.HelpLink)) if err != nil { return nil, err } @@ -74,7 +75,7 @@ func (c *Commands) ChangePrivacyPolicy(ctx context.Context, resourceOwner string } orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PrivacyPolicyWriteModel.WriteModel) - changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.TOSLink, policy.PrivacyLink) + changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink) if !hasChanged { return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4N9fs", "Errors.Org.PrivacyPolicy.NotChanged") } diff --git a/internal/command/org_policy_privacy_model.go b/internal/command/org_policy_privacy_model.go index 171b48f75a..d5ff331dbd 100644 --- a/internal/command/org_policy_privacy_model.go +++ b/internal/command/org_policy_privacy_model.go @@ -57,7 +57,8 @@ func (wm *OrgPrivacyPolicyWriteModel) NewChangedEvent( ctx context.Context, aggregate *eventstore.Aggregate, tosLink, - privacyLink string, + privacyLink, + helpLink string, ) (*org.PrivacyPolicyChangedEvent, bool) { changes := make([]policy.PrivacyPolicyChanges, 0) @@ -67,6 +68,9 @@ func (wm *OrgPrivacyPolicyWriteModel) NewChangedEvent( if wm.PrivacyLink != privacyLink { changes = append(changes, policy.ChangePrivacyLink(privacyLink)) } + if wm.HelpLink != helpLink { + changes = append(changes, policy.ChangeHelpLink(helpLink)) + } if len(changes) == 0 { return nil, false } diff --git a/internal/command/org_policy_privacy_test.go b/internal/command/org_policy_privacy_test.go index ba762371cb..36a8b1ba81 100644 --- a/internal/command/org_policy_privacy_test.go +++ b/internal/command/org_policy_privacy_test.go @@ -46,6 +46,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -63,6 +64,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), @@ -74,6 +76,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -93,6 +96,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), }, @@ -105,6 +109,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -115,6 +120,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { }, TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, }, @@ -131,6 +137,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, "", "", + "", ), ), }, @@ -143,6 +150,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "", PrivacyLink: "", + HelpLink: "", }, }, res: res{ @@ -153,6 +161,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) { }, TOSLink: "", PrivacyLink: "", + HelpLink: "", }, }, }, @@ -207,6 +216,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -227,6 +237,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -244,6 +255,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), @@ -255,6 +267,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLink", PrivacyLink: "PrivacyLink", + HelpLink: "HelpLink", }, }, res: res{ @@ -272,13 +285,14 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), expectPush( []*repository.Event{ eventFromEventPusher( - newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange"), + newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange", "HelpLinkChange"), ), }, ), @@ -290,6 +304,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "TOSLinkChange", PrivacyLink: "PrivacyLinkChange", + HelpLink: "HelpLinkChange", }, }, res: res{ @@ -300,6 +315,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { }, TOSLink: "TOSLinkChange", PrivacyLink: "PrivacyLinkChange", + HelpLink: "HelpLinkChange", }, }, }, @@ -314,13 +330,14 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), expectPush( []*repository.Event{ eventFromEventPusher( - newPrivacyPolicyChangedEvent(context.Background(), "org1", "", ""), + newPrivacyPolicyChangedEvent(context.Background(), "org1", "", "", ""), ), }, ), @@ -332,6 +349,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { policy: &domain.PrivacyPolicy{ TOSLink: "", PrivacyLink: "", + HelpLink: "", }, }, res: res{ @@ -342,6 +360,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) { }, TOSLink: "", PrivacyLink: "", + HelpLink: "", }, }, }, @@ -424,6 +443,7 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) { &org.NewAggregate("org1", "org1").Aggregate, "TOSLink", "PrivacyLink", + "HelpLink", ), ), ), @@ -467,12 +487,13 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) { } } -func newPrivacyPolicyChangedEvent(ctx context.Context, orgID string, tosLink, privacyLink string) *org.PrivacyPolicyChangedEvent { +func newPrivacyPolicyChangedEvent(ctx context.Context, orgID string, tosLink, privacyLink, helpLink string) *org.PrivacyPolicyChangedEvent { event, _ := org.NewPrivacyPolicyChangedEvent(ctx, &org.NewAggregate(orgID, orgID).Aggregate, []policy.PrivacyPolicyChanges{ policy.ChangeTOSLink(tosLink), policy.ChangePrivacyLink(privacyLink), + policy.ChangeHelpLink(helpLink), }, ) return event diff --git a/internal/command/policy_privacy_model.go b/internal/command/policy_privacy_model.go index 07e489f447..9dbd6722af 100644 --- a/internal/command/policy_privacy_model.go +++ b/internal/command/policy_privacy_model.go @@ -11,6 +11,7 @@ type PrivacyPolicyWriteModel struct { TOSLink string PrivacyLink string + HelpLink string State domain.PolicyState } @@ -20,6 +21,7 @@ func (wm *PrivacyPolicyWriteModel) Reduce() error { case *policy.PrivacyPolicyAddedEvent: wm.TOSLink = e.TOSLink wm.PrivacyLink = e.PrivacyLink + wm.HelpLink = e.HelpLink wm.State = domain.PolicyStateActive case *policy.PrivacyPolicyChangedEvent: if e.PrivacyLink != nil { @@ -28,6 +30,9 @@ func (wm *PrivacyPolicyWriteModel) Reduce() error { if e.TOSLink != nil { wm.TOSLink = *e.TOSLink } + if e.HelpLink != nil { + wm.HelpLink = *e.HelpLink + } case *policy.PrivacyPolicyRemovedEvent: wm.State = domain.PolicyStateRemoved } diff --git a/internal/command/user.go b/internal/command/user.go index f6df852a7e..cb8ef5236f 100644 --- a/internal/command/user.go +++ b/internal/command/user.go @@ -38,7 +38,7 @@ func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName s orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID) if err != nil { - return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.OrgIAM.NotExisting") + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.OrgIAMPolicy.NotExisting") } if err := CheckOrgIAMPolicyForUserName(userName, orgIAMPolicy); err != nil { @@ -188,7 +188,7 @@ func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string, orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, existingUser.ResourceOwner) if err != nil { - return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.OrgIAM.NotExisting") + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.OrgIAMPolicy.NotExisting") } var events []eventstore.Command userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel) diff --git a/internal/command/user_human.go b/internal/command/user_human.go index e93fe3183b..370f77a594 100644 --- a/internal/command/user_human.go +++ b/internal/command/user_human.go @@ -34,7 +34,7 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum } pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID) if err != nil { - return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound") + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexityPolicy.NotFound") } events, addedHuman, err := c.addHuman(ctx, orgID, human, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator) if err != nil { @@ -63,7 +63,7 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain. } pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID) if err != nil { - return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexity.NotFound") + return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexityPolicy.NotFound") } events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator) if err != nil { @@ -128,7 +128,7 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai } pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID) if err != nil { - return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound") + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexityPolicy.NotFound") } loginPolicy, err := c.getOrgLoginPolicy(ctx, orgID) if err != nil { diff --git a/internal/command/user_human_otp.go b/internal/command/user_human_otp.go index 5eab8c9b9d..5e0ad5cccc 100644 --- a/internal/command/user_human_otp.go +++ b/internal/command/user_human_otp.go @@ -28,7 +28,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string orgPolicy, err := c.getOrgIAMPolicy(ctx, org.AggregateID) if err != nil { logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname") - return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.OrgIAM.NotFound") + return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.OrgIAMPolicy.NotFound") } otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner) if err != nil { diff --git a/internal/domain/custom_login_text.go b/internal/domain/custom_login_text.go index 386eaf22ba..df7c2a9d77 100644 --- a/internal/domain/custom_login_text.go +++ b/internal/domain/custom_login_text.go @@ -294,7 +294,6 @@ const ( LoginKeyFooterTOS = LoginKeyFooter + "Tos" LoginKeyFooterPrivacyPolicy = LoginKeyFooter + "PrivacyPolicy" LoginKeyFooterHelp = LoginKeyFooter + "Help" - LoginKeyFooterHelpLink = LoginKeyFooter + "HelpLink" ) type CustomLoginText struct { @@ -637,7 +636,6 @@ type FooterText struct { TOS string PrivacyPolicy string Help string - HelpLink string } type PasswordlessPromptScreenText struct { diff --git a/internal/domain/policy_privacy.go b/internal/domain/policy_privacy.go index 9eb58f3897..eec1ff8c26 100644 --- a/internal/domain/policy_privacy.go +++ b/internal/domain/policy_privacy.go @@ -12,4 +12,5 @@ type PrivacyPolicy struct { TOSLink string PrivacyLink string + HelpLink string } diff --git a/internal/iam/repository/view/model/custom_text.go b/internal/iam/repository/view/model/custom_text.go index 93fa3b3b37..81bb6bbef8 100644 --- a/internal/iam/repository/view/model/custom_text.go +++ b/internal/iam/repository/view/model/custom_text.go @@ -894,7 +894,4 @@ func footerKeyToDomain(text *CustomTextView, result *domain.CustomLoginText) { if text.Key == domain.LoginKeyFooterHelp { result.Footer.Help = text.Text } - if text.Key == domain.LoginKeyFooterHelpLink { - result.Footer.HelpLink = text.Text - } } diff --git a/internal/notification/repository/eventsourcing/handler/notify_user.go b/internal/notification/repository/eventsourcing/handler/notify_user.go index 4cef14f6f8..ad4727c3df 100644 --- a/internal/notification/repository/eventsourcing/handler/notify_user.go +++ b/internal/notification/repository/eventsourcing/handler/notify_user.go @@ -3,14 +3,14 @@ package handler import ( "context" + "github.com/caos/logging" + caos_errs "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1" + v1 "github.com/caos/zitadel/internal/eventstore/v1" es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk" org_view "github.com/caos/zitadel/internal/org/repository/view" query2 "github.com/caos/zitadel/internal/query" - "github.com/caos/logging" - es_models "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/spooler" @@ -163,23 +163,16 @@ func (u *NotifyUser) ProcessOrg(event *es_models.Event) (err error) { } func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *es_models.Event) error { - org, err := u.getOrgByID(context.Background(), event.ResourceOwner) + userLoginMustBeDomain, _, domains, err := u.loginNameInformation(context.Background(), event.ResourceOwner) if err != nil { return err } - policy := new(query2.OrgIAMPolicy) - if policy == nil { - policy, err = u.getDefaultOrgIAMPolicy(context.Background()) - if err != nil { - return err - } - } users, err := u.view.NotifyUsersByOrgID(event.AggregateID) if err != nil { return err } for _, user := range users { - user.SetLoginNames(policy, org.Domains) + user.SetLoginNames(userLoginMustBeDomain, domains) err := u.view.PutNotifyUser(user, event) if err != nil { return err @@ -189,16 +182,11 @@ func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *es_models.Event) error { } func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error { - org, err := u.getOrgByID(context.Background(), event.ResourceOwner) + userLoginMustBeDomain, primaryDomain, _, err := u.loginNameInformation(context.Background(), event.ResourceOwner) if err != nil { return err } - - policy, err := u.getDefaultOrgIAMPolicy(context.Background()) - if err != nil { - return err - } - if !policy.UserLoginMustBeDomain { + if !userLoginMustBeDomain { return nil } users, err := u.view.NotifyUsersByOrgID(event.AggregateID) @@ -206,7 +194,7 @@ func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) e return err } for _, user := range users { - user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain) err := u.view.PutNotifyUser(user, event) if err != nil { return err @@ -216,17 +204,12 @@ func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) e } func (u *NotifyUser) fillLoginNames(user *view_model.NotifyUser) (err error) { - org, err := u.getOrgByID(context.Background(), user.ResourceOwner) + userLoginMustBeDomain, primaryDomain, domains, err := u.loginNameInformation(context.Background(), user.ResourceOwner) if err != nil { return err } - - policy, err := u.getDefaultOrgIAMPolicy(context.Background()) - if err != nil { - return err - } - user.SetLoginNames(policy, org.Domains) - user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + user.SetLoginNames(userLoginMustBeDomain, domains) + user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain) return nil } @@ -261,6 +244,17 @@ func (u *NotifyUser) getOrgByID(ctx context.Context, orgID string) (*org_model.O return org_es_model.OrgToModel(esOrg), nil } -func (u *NotifyUser) getDefaultOrgIAMPolicy(ctx context.Context) (*query2.OrgIAMPolicy, error) { - return u.queries.DefaultOrgIAMPolicy(ctx) +func (u *NotifyUser) loginNameInformation(ctx context.Context, orgID string) (userLoginMustBeDomain bool, primaryDomain string, domains []*org_model.OrgDomain, err error) { + org, err := u.getOrgByID(ctx, orgID) + if err != nil { + return false, "", nil, err + } + if org.OrgIamPolicy == nil { + policy, err := u.queries.DefaultOrgIAMPolicy(ctx) + if err != nil { + return false, "", nil, err + } + userLoginMustBeDomain = policy.UserLoginMustBeDomain + } + return userLoginMustBeDomain, org.GetPrimaryDomain().Domain, org.Domains, nil } diff --git a/internal/query/custom_text.go b/internal/query/custom_text.go index 676ea8a084..a938e51497 100644 --- a/internal/query/custom_text.go +++ b/internal/query/custom_text.go @@ -1101,7 +1101,4 @@ func footerKeyToDomain(text *CustomText, result *domain.CustomLoginText) { if text.Key == domain.LoginKeyFooterHelp { result.Footer.Help = text.Text } - if text.Key == domain.LoginKeyFooterHelpLink { - result.Footer.HelpLink = text.Text - } } diff --git a/internal/query/privacy_policy.go b/internal/query/privacy_policy.go index 98fe95d190..b8544706df 100644 --- a/internal/query/privacy_policy.go +++ b/internal/query/privacy_policy.go @@ -24,6 +24,7 @@ type PrivacyPolicy struct { TOSLink string PrivacyLink string + HelpLink string IsDefault bool } @@ -64,6 +65,10 @@ var ( name: projection.PrivacyPolicyTOSLinkCol, table: privacyTable, } + PrivacyColHelpLink = Column{ + name: projection.PrivacyPolicyHelpLinkCol, + table: privacyTable, + } PrivacyColIsDefault = Column{ name: projection.PrivacyPolicyIsDefaultCol, table: privacyTable, @@ -125,6 +130,7 @@ func preparePrivacyPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*PrivacyPoli PrivacyColResourceOwner.identifier(), PrivacyColPrivacyLink.identifier(), PrivacyColTOSLink.identifier(), + PrivacyColHelpLink.identifier(), PrivacyColIsDefault.identifier(), PrivacyColState.identifier(), ). @@ -139,6 +145,7 @@ func preparePrivacyPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*PrivacyPoli &policy.ResourceOwner, &policy.PrivacyLink, &policy.TOSLink, + &policy.HelpLink, &policy.IsDefault, &policy.State, ) @@ -151,3 +158,12 @@ func preparePrivacyPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*PrivacyPoli return policy, nil } } + +func (p *PrivacyPolicy) ToDomain() *domain.PrivacyPolicy { + return &domain.PrivacyPolicy{ + TOSLink: p.TOSLink, + PrivacyLink: p.PrivacyLink, + HelpLink: p.HelpLink, + Default: p.IsDefault, + } +} diff --git a/internal/query/privacy_policy_test.go b/internal/query/privacy_policy_test.go index 389ad4d46e..c554bbbdea 100644 --- a/internal/query/privacy_policy_test.go +++ b/internal/query/privacy_policy_test.go @@ -35,6 +35,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { ` projections.privacy_policies.resource_owner,`+ ` projections.privacy_policies.privacy_link,`+ ` projections.privacy_policies.tos_link,`+ + ` projections.privacy_policies.help_link,`+ ` projections.privacy_policies.is_default,`+ ` projections.privacy_policies.state`+ ` FROM projections.privacy_policies`), @@ -43,7 +44,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { ), err: func(err error) (error, bool) { if !errs.IsNotFound(err) { - return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + return fmt.Errorf("err should be NotFoundError got: %w", err), false } return nil, true }, @@ -62,6 +63,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { ` projections.privacy_policies.resource_owner,`+ ` projections.privacy_policies.privacy_link,`+ ` projections.privacy_policies.tos_link,`+ + ` projections.privacy_policies.help_link,`+ ` projections.privacy_policies.is_default,`+ ` projections.privacy_policies.state`+ ` FROM projections.privacy_policies`), @@ -73,6 +75,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { "resource_owner", "privacy_link", "tos_link", + "help_link", "is_default", "state", }, @@ -84,6 +87,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { "ro", "privacy.ch", "tos.ch", + "help.ch", true, domain.PolicyStateActive, }, @@ -98,6 +102,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { State: domain.PolicyStateActive, PrivacyLink: "privacy.ch", TOSLink: "tos.ch", + HelpLink: "help.ch", IsDefault: true, }, }, @@ -113,6 +118,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) { ` projections.privacy_policies.resource_owner,`+ ` projections.privacy_policies.privacy_link,`+ ` projections.privacy_policies.tos_link,`+ + ` projections.privacy_policies.help_link,`+ ` projections.privacy_policies.is_default,`+ ` projections.privacy_policies.state`+ ` FROM projections.privacy_policies`), diff --git a/internal/query/project_grant_member.go b/internal/query/project_grant_member.go index 154dffae23..4673bc6db9 100644 --- a/internal/query/project_grant_member.go +++ b/internal/query/project_grant_member.go @@ -58,15 +58,21 @@ var ( type ProjectGrantMembersQuery struct { MembersQuery - ProjectID, GrantID string + ProjectID, GrantID, OrgID string } func (q *ProjectGrantMembersQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder { return q.MembersQuery. toQuery(query). - Where(sq.Eq{ - ProjectGrantMemberProjectID.identifier(): q.ProjectID, - ProjectGrantMemberGrantID.identifier(): q.GrantID, + Where(sq.And{ + sq.Eq{ + ProjectGrantMemberProjectID.identifier(): q.ProjectID, + ProjectGrantMemberGrantID.identifier(): q.GrantID, + }, + sq.Or{ + sq.Eq{ProjectGrantColumnResourceOwner.identifier(): q.OrgID}, + sq.Eq{ProjectGrantColumnGrantedOrgID.identifier(): q.OrgID}, + }, }) } @@ -117,6 +123,7 @@ func prepareProjectGrantMembersQuery() (sq.SelectBuilder, func(*sql.Rows) (*Memb LeftJoin(join(HumanUserIDCol, ProjectGrantMemberUserID)). LeftJoin(join(MachineUserIDCol, ProjectGrantMemberUserID)). LeftJoin(join(LoginNameUserIDCol, ProjectGrantMemberUserID)). + LeftJoin(join(ProjectGrantColumnGrantID, ProjectGrantMemberGrantID)). Where( sq.Eq{LoginNameIsPrimaryCol.identifier(): true}, ).PlaceholderFormat(sq.Dollar), diff --git a/internal/query/project_grant_member_test.go b/internal/query/project_grant_member_test.go index 9e7e0102a1..58069c2908 100644 --- a/internal/query/project_grant_member_test.go +++ b/internal/query/project_grant_member_test.go @@ -34,6 +34,8 @@ var ( "ON members.user_id = projections.users_machines.user_id " + "LEFT JOIN projections.login_names " + "ON members.user_id = projections.login_names.user_id " + + "LEFT JOIN projections.project_grants " + + "ON members.grant_id = projections.project_grants.grant_id " + "WHERE projections.login_names.is_primary = $1") projectGrantMembersColumns = []string{ "creation_date", diff --git a/internal/query/projection/flow.go b/internal/query/projection/flow.go index eeed193e13..2e4dd699cf 100644 --- a/internal/query/projection/flow.go +++ b/internal/query/projection/flow.go @@ -76,6 +76,7 @@ func (p *FlowProjection) reduceTriggerActionsSetEventType(event eventstore.Event []handler.Condition{ handler.NewCond(FlowTypeCol, e.FlowType), handler.NewCond(FlowTriggerTypeCol, e.TriggerType), + handler.NewCond(FlowResourceOwnerCol, e.Aggregate().ResourceOwner), }, ) for i, id := range e.ActionIDs { @@ -104,6 +105,7 @@ func (p *FlowProjection) reduceFlowClearedEventType(event eventstore.Event) (*ha e, []handler.Condition{ handler.NewCond(FlowTypeCol, e.FlowType), + handler.NewCond(FlowResourceOwnerCol, e.Aggregate().ResourceOwner), }, ), nil } diff --git a/internal/query/projection/flow_test.go b/internal/query/projection/flow_test.go index 6124875d79..8d23f95672 100644 --- a/internal/query/projection/flow_test.go +++ b/internal/query/projection/flow_test.go @@ -39,10 +39,11 @@ func TestFlowProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1) AND (trigger_type = $2)", + expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1) AND (trigger_type = $2) AND (resource_owner = $3)", expectedArgs: []interface{}{ domain.FlowTypeExternalAuthentication, domain.TriggerTypePostAuthentication, + "ro-id", }, }, { @@ -93,9 +94,10 @@ func TestFlowProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1)", + expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1) AND (resource_owner = $2)", expectedArgs: []interface{}{ domain.FlowTypeExternalAuthentication, + "ro-id", }, }, }, diff --git a/internal/query/projection/privacy_policy.go b/internal/query/projection/privacy_policy.go index 2a36ac2cc8..0f722fe398 100644 --- a/internal/query/projection/privacy_policy.go +++ b/internal/query/projection/privacy_policy.go @@ -26,6 +26,7 @@ const ( PrivacyPolicyInstanceIDCol = "instance_id" PrivacyPolicyPrivacyLinkCol = "privacy_link" PrivacyPolicyTOSLinkCol = "tos_link" + PrivacyPolicyHelpLinkCol = "help_link" ) type PrivacyPolicyProjection struct { @@ -48,6 +49,7 @@ func NewPrivacyPolicyProjection(ctx context.Context, config crdb.StatementHandle crdb.NewColumn(PrivacyPolicyInstanceIDCol, crdb.ColumnTypeText), crdb.NewColumn(PrivacyPolicyPrivacyLinkCol, crdb.ColumnTypeText), crdb.NewColumn(PrivacyPolicyTOSLinkCol, crdb.ColumnTypeText), + crdb.NewColumn(PrivacyPolicyHelpLinkCol, crdb.ColumnTypeText), }, crdb.NewPrimaryKey(PrivacyPolicyInstanceIDCol, PrivacyPolicyIDCol), ), @@ -114,6 +116,7 @@ func (p *PrivacyPolicyProjection) reduceAdded(event eventstore.Event) (*handler. handler.NewCol(PrivacyPolicyStateCol, domain.PolicyStateActive), handler.NewCol(PrivacyPolicyPrivacyLinkCol, policyEvent.PrivacyLink), handler.NewCol(PrivacyPolicyTOSLinkCol, policyEvent.TOSLink), + handler.NewCol(PrivacyPolicyHelpLinkCol, policyEvent.HelpLink), handler.NewCol(PrivacyPolicyIsDefaultCol, isDefault), handler.NewCol(PrivacyPolicyResourceOwnerCol, policyEvent.Aggregate().ResourceOwner), handler.NewCol(PrivacyPolicyInstanceIDCol, policyEvent.Aggregate().InstanceID), @@ -140,6 +143,9 @@ func (p *PrivacyPolicyProjection) reduceChanged(event eventstore.Event) (*handle if policyEvent.TOSLink != nil { cols = append(cols, handler.NewCol(PrivacyPolicyTOSLinkCol, *policyEvent.TOSLink)) } + if policyEvent.HelpLink != nil { + cols = append(cols, handler.NewCol(PrivacyPolicyHelpLinkCol, *policyEvent.HelpLink)) + } return crdb.NewUpdateStatement( &policyEvent, cols, diff --git a/internal/query/projection/privacy_policy_test.go b/internal/query/projection/privacy_policy_test.go index 9748f6a3d8..e74c03cb2b 100644 --- a/internal/query/projection/privacy_policy_test.go +++ b/internal/query/projection/privacy_policy_test.go @@ -30,7 +30,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { org.AggregateType, []byte(`{ "tosLink": "http://tos.link", - "privacyLink": "http://privacy.link" + "privacyLink": "http://privacy.link", + "helpLink": "http://help.link" }`), ), org.PrivacyPolicyAddedEventMapper), }, @@ -43,7 +44,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", + expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -52,6 +53,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { domain.PolicyStateActive, "http://privacy.link", "http://tos.link", + "http://help.link", false, "ro-id", "instance-id", @@ -70,7 +72,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { org.AggregateType, []byte(`{ "tosLink": "http://tos.link", - "privacyLink": "http://privacy.link" + "privacyLink": "http://privacy.link", + "helpLink": "http://help.link" }`), ), org.PrivacyPolicyChangedEventMapper), }, @@ -82,12 +85,13 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link) = ($1, $2, $3, $4) WHERE (id = $5)", + expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link, help_link) = ($1, $2, $3, $4, $5) WHERE (id = $6)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "http://privacy.link", "http://tos.link", + "http://help.link", "agg-id", }, }, @@ -131,7 +135,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { iam.AggregateType, []byte(`{ "tosLink": "http://tos.link", - "privacyLink": "http://privacy.link" + "privacyLink": "http://privacy.link", + "helpLink": "http://help.link" }`), ), iam.PrivacyPolicyAddedEventMapper), }, @@ -143,7 +148,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", + expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", expectedArgs: []interface{}{ anyArg{}, anyArg{}, @@ -152,6 +157,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { domain.PolicyStateActive, "http://privacy.link", "http://tos.link", + "http://help.link", true, "ro-id", "instance-id", @@ -170,7 +176,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { iam.AggregateType, []byte(`{ "tosLink": "http://tos.link", - "privacyLink": "http://privacy.link" + "privacyLink": "http://privacy.link", + "helpLink": "http://help.link" }`), ), iam.PrivacyPolicyChangedEventMapper), }, @@ -182,12 +189,13 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link) = ($1, $2, $3, $4) WHERE (id = $5)", + expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link, help_link) = ($1, $2, $3, $4, $5) WHERE (id = $6)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "http://privacy.link", "http://tos.link", + "http://help.link", "agg-id", }, }, diff --git a/internal/query/user_grant.go b/internal/query/user_grant.go index 5dbe1f9ab1..dd9d0f8bde 100644 --- a/internal/query/user_grant.go +++ b/internal/query/user_grant.go @@ -25,15 +25,16 @@ type UserGrant struct { GrantID string State domain.UserGrantState - UserID string - Username string - UserType domain.UserType - UserResourceOwner string - FirstName string - LastName string - Email string - DisplayName string - AvatarURL string + UserID string + Username string + UserType domain.UserType + UserResourceOwner string + FirstName string + LastName string + Email string + DisplayName string + AvatarURL string + PreferredLoginName string ResourceOwner string OrgName string @@ -255,6 +256,7 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro HumanEmailCol.identifier(), HumanDisplayNameCol.identifier(), HumanAvatarURLCol.identifier(), + LoginNameNameCol.identifier(), UserGrantResourceOwner.identifier(), OrgColumnName.identifier(), @@ -268,20 +270,24 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro LeftJoin(join(HumanUserIDCol, UserGrantUserID)). LeftJoin(join(OrgColumnID, UserGrantResourceOwner)). LeftJoin(join(ProjectColumnID, UserGrantProjectID)). - PlaceholderFormat(sq.Dollar), + LeftJoin(join(LoginNameUserIDCol, UserGrantUserID)). + Where( + sq.Eq{LoginNameIsPrimaryCol.identifier(): true}, + ).PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*UserGrant, error) { g := new(UserGrant) var ( - roles = pq.StringArray{} - username sql.NullString - firstName sql.NullString - userType sql.NullInt32 - userOwner sql.NullString - lastName sql.NullString - email sql.NullString - displayName sql.NullString - avatarURL sql.NullString + roles = pq.StringArray{} + username sql.NullString + firstName sql.NullString + userType sql.NullInt32 + userOwner sql.NullString + lastName sql.NullString + email sql.NullString + displayName sql.NullString + avatarURL sql.NullString + preferredLoginName sql.NullString orgName sql.NullString orgDomain sql.NullString @@ -307,6 +313,7 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro &email, &displayName, &avatarURL, + &preferredLoginName, &g.ResourceOwner, &orgName, @@ -331,6 +338,7 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro g.Email = email.String g.DisplayName = displayName.String g.AvatarURL = avatarURL.String + g.PreferredLoginName = preferredLoginName.String g.OrgName = orgName.String g.OrgPrimaryDomain = orgDomain.String g.ProjectName = projectName.String @@ -358,6 +366,7 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e HumanEmailCol.identifier(), HumanDisplayNameCol.identifier(), HumanAvatarURLCol.identifier(), + LoginNameNameCol.identifier(), UserGrantResourceOwner.identifier(), OrgColumnName.identifier(), @@ -373,7 +382,10 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e LeftJoin(join(HumanUserIDCol, UserGrantUserID)). LeftJoin(join(OrgColumnID, UserGrantResourceOwner)). LeftJoin(join(ProjectColumnID, UserGrantProjectID)). - PlaceholderFormat(sq.Dollar), + LeftJoin(join(LoginNameUserIDCol, UserGrantUserID)). + Where( + sq.Eq{LoginNameIsPrimaryCol.identifier(): true}, + ).PlaceholderFormat(sq.Dollar), func(rows *sql.Rows) (*UserGrants, error) { userGrants := make([]*UserGrant, 0) var count uint64 @@ -381,15 +393,16 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e g := new(UserGrant) var ( - roles = pq.StringArray{} - username sql.NullString - userType sql.NullInt32 - userOwner sql.NullString - firstName sql.NullString - lastName sql.NullString - email sql.NullString - displayName sql.NullString - avatarURL sql.NullString + roles = pq.StringArray{} + username sql.NullString + userType sql.NullInt32 + userOwner sql.NullString + firstName sql.NullString + lastName sql.NullString + email sql.NullString + displayName sql.NullString + avatarURL sql.NullString + preferredLoginName sql.NullString orgName sql.NullString orgDomain sql.NullString @@ -415,6 +428,7 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e &email, &displayName, &avatarURL, + &preferredLoginName, &g.ResourceOwner, &orgName, @@ -438,6 +452,7 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e g.Email = email.String g.DisplayName = displayName.String g.AvatarURL = avatarURL.String + g.PreferredLoginName = preferredLoginName.String g.OrgName = orgName.String g.OrgPrimaryDomain = orgDomain.String g.ProjectName = projectName.String diff --git a/internal/query/user_grant_test.go b/internal/query/user_grant_test.go index f84a5b00ab..da4b30e4ca 100644 --- a/internal/query/user_grant_test.go +++ b/internal/query/user_grant_test.go @@ -32,6 +32,7 @@ var ( ", projections.users_humans.email" + ", projections.users_humans.display_name" + ", projections.users_humans.avatar_key" + + ", projections.login_names.login_name" + ", projections.user_grants.resource_owner" + ", projections.orgs.name" + ", projections.orgs.primary_domain" + @@ -41,7 +42,9 @@ var ( " LEFT JOIN projections.users ON projections.user_grants.user_id = projections.users.id" + " LEFT JOIN projections.users_humans ON projections.user_grants.user_id = projections.users_humans.user_id" + " LEFT JOIN projections.orgs ON projections.user_grants.resource_owner = projections.orgs.id" + - " LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id") + " LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id" + + " LEFT JOIN projections.login_names ON projections.user_grants.user_id = projections.login_names.user_id" + + " WHERE projections.login_names.is_primary = $1") userGrantCols = []string{ "id", "creation_date", @@ -59,6 +62,7 @@ var ( "email", "display_name", "avatar_key", + "login_name", "resource_owner", //user_grant resource owner "name", //org name "primary_domain", @@ -82,6 +86,7 @@ var ( ", projections.users_humans.email" + ", projections.users_humans.display_name" + ", projections.users_humans.avatar_key" + + ", projections.login_names.login_name" + ", projections.user_grants.resource_owner" + ", projections.orgs.name" + ", projections.orgs.primary_domain" + @@ -92,7 +97,9 @@ var ( " LEFT JOIN projections.users ON projections.user_grants.user_id = projections.users.id" + " LEFT JOIN projections.users_humans ON projections.user_grants.user_id = projections.users_humans.user_id" + " LEFT JOIN projections.orgs ON projections.user_grants.resource_owner = projections.orgs.id" + - " LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id") + " LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id" + + " LEFT JOIN projections.login_names ON projections.user_grants.user_id = projections.login_names.user_id" + + " WHERE projections.login_names.is_primary = $1") userGrantsCols = append( userGrantCols, "count", @@ -152,6 +159,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", "org-name", "primary-domain", @@ -161,27 +169,28 @@ func Test_UserGrantPrepares(t *testing.T) { ), }, object: &UserGrant{ - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeHuman, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, }, { @@ -208,6 +217,7 @@ func Test_UserGrantPrepares(t *testing.T) { nil, nil, nil, + "login-name", "ro", "org-name", "primary-domain", @@ -217,27 +227,28 @@ func Test_UserGrantPrepares(t *testing.T) { ), }, object: &UserGrant{ - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeMachine, - UserResourceOwner: "resource-owner", - FirstName: "", - LastName: "", - Email: "", - DisplayName: "", - AvatarURL: "", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeMachine, + UserResourceOwner: "resource-owner", + FirstName: "", + LastName: "", + Email: "", + DisplayName: "", + AvatarURL: "", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, }, { @@ -264,6 +275,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", nil, nil, @@ -273,27 +285,28 @@ func Test_UserGrantPrepares(t *testing.T) { ), }, object: &UserGrant{ - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeHuman, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "", - OrgPrimaryDomain: "", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "", + OrgPrimaryDomain: "", + ProjectID: "project-id", + ProjectName: "project-name", }, }, { @@ -320,6 +333,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", "org-name", "primary-domain", @@ -329,27 +343,86 @@ func Test_UserGrantPrepares(t *testing.T) { ), }, object: &UserGrant{ - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeHuman, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "", + }, + }, + { + name: "prepareUserGrantQuery (no loginname) found", + prepare: prepareUserGrantQuery, + want: want{ + sqlExpectations: mockQuery( + userGrantStmt, + userGrantCols, + []driver.Value{ + "id", + testNow, + testNow, + 20211111, + "grant-id", + pq.StringArray{"role-key"}, + domain.UserGrantStateActive, + "user-id", + "username", + domain.UserTypeHuman, + "resource-owner", + "first-name", + "last-name", + "email", + "display-name", + "avatar-key", + nil, + "ro", + "org-name", + "primary-domain", + "project-id", + "project-name", + }, + ), + }, + object: &UserGrant{ + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, }, { @@ -406,6 +479,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", "org-name", "primary-domain", @@ -421,27 +495,28 @@ func Test_UserGrantPrepares(t *testing.T) { }, UserGrants: []*UserGrant{ { - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeHuman, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, }, }, @@ -471,6 +546,7 @@ func Test_UserGrantPrepares(t *testing.T) { nil, nil, nil, + "login-name", "ro", "org-name", "primary-domain", @@ -486,27 +562,28 @@ func Test_UserGrantPrepares(t *testing.T) { }, UserGrants: []*UserGrant{ { - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeMachine, - UserResourceOwner: "resource-owner", - FirstName: "", - LastName: "", - Email: "", - DisplayName: "", - AvatarURL: "", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeMachine, + UserResourceOwner: "resource-owner", + FirstName: "", + LastName: "", + Email: "", + DisplayName: "", + AvatarURL: "", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, }, }, @@ -536,6 +613,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", nil, nil, @@ -551,27 +629,28 @@ func Test_UserGrantPrepares(t *testing.T) { }, UserGrants: []*UserGrant{ { - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeMachine, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "", - OrgPrimaryDomain: "", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeMachine, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "", + OrgPrimaryDomain: "", + ProjectID: "project-id", + ProjectName: "project-name", }, }, }, @@ -601,6 +680,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", "org-name", "primary-domain", @@ -616,27 +696,95 @@ func Test_UserGrantPrepares(t *testing.T) { }, UserGrants: []*UserGrant{ { - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeHuman, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "", + }, + }, + }, + }, + { + name: "prepareUserGrantsQuery one grant (no loginname)", + prepare: prepareUserGrantsQuery, + want: want{ + sqlExpectations: mockQueries( + userGrantsStmt, + userGrantsCols, + [][]driver.Value{ + { + "id", + testNow, + testNow, + 20211111, + "grant-id", + pq.StringArray{"role-key"}, + domain.UserGrantStateActive, + "user-id", + "username", + domain.UserTypeHuman, + "resource-owner", + "first-name", + "last-name", + "email", + "display-name", + "avatar-key", + nil, + "ro", + "org-name", + "primary-domain", + "project-id", + "project-name", + }, + }, + ), + }, + object: &UserGrants{ + SearchResponse: SearchResponse{ + Count: 1, + }, + UserGrants: []*UserGrant{ + { + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, }, }, @@ -666,6 +814,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", "org-name", "primary-domain", @@ -689,6 +838,7 @@ func Test_UserGrantPrepares(t *testing.T) { "email", "display-name", "avatar-key", + "login-name", "ro", "org-name", "primary-domain", @@ -704,50 +854,52 @@ func Test_UserGrantPrepares(t *testing.T) { }, UserGrants: []*UserGrant{ { - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeHuman, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, { - ID: "id", - CreationDate: testNow, - ChangeDate: testNow, - Sequence: 20211111, - Roles: []string{"role-key"}, - GrantID: "grant-id", - State: domain.UserGrantStateActive, - UserID: "user-id", - Username: "username", - UserType: domain.UserTypeHuman, - UserResourceOwner: "resource-owner", - FirstName: "first-name", - LastName: "last-name", - Email: "email", - DisplayName: "display-name", - AvatarURL: "avatar-key", - ResourceOwner: "ro", - OrgName: "org-name", - OrgPrimaryDomain: "primary-domain", - ProjectID: "project-id", - ProjectName: "project-name", + ID: "id", + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211111, + Roles: []string{"role-key"}, + GrantID: "grant-id", + State: domain.UserGrantStateActive, + UserID: "user-id", + Username: "username", + UserType: domain.UserTypeHuman, + UserResourceOwner: "resource-owner", + FirstName: "first-name", + LastName: "last-name", + Email: "email", + DisplayName: "display-name", + AvatarURL: "avatar-key", + PreferredLoginName: "login-name", + ResourceOwner: "ro", + OrgName: "org-name", + OrgPrimaryDomain: "primary-domain", + ProjectID: "project-id", + ProjectName: "project-name", }, }, }, diff --git a/internal/query/user_membership.go b/internal/query/user_membership.go index 8d70156675..59fbfbe69b 100644 --- a/internal/query/user_membership.go +++ b/internal/query/user_membership.go @@ -70,6 +70,14 @@ func NewMembershipOrgIDQuery(value string) (SearchQuery, error) { return NewTextQuery(membershipOrgID, value, TextEquals) } +func NewMembershipResourceOwnersSearchQuery(ids ...string) (SearchQuery, error) { + list := make([]interface{}, len(ids)) + for i, value := range ids { + list[i] = value + } + return NewListQuery(membershipResourceOwner, list, ListIn) +} + func NewMembershipProjectIDQuery(value string) (SearchQuery, error) { return NewTextQuery(membershipProjectID, value, TextEquals) } diff --git a/internal/query/zitadel_permission.go b/internal/query/zitadel_permission.go index 0b7672a18f..51261f749b 100644 --- a/internal/query/zitadel_permission.go +++ b/internal/query/zitadel_permission.go @@ -6,13 +6,17 @@ import ( "github.com/caos/zitadel/internal/domain" ) -func (q *Queries) MyZitadelPermissions(ctx context.Context, userID string) (*domain.Permissions, error) { +func (q *Queries) MyZitadelPermissions(ctx context.Context, orgID, userID string) (*domain.Permissions, error) { userIDQuery, err := NewMembershipUserIDQuery(userID) if err != nil { return nil, err } + orgIDsQuery, err := NewMembershipResourceOwnersSearchQuery(orgID, domain.IAMID) + if err != nil { + return nil, err + } memberships, err := q.Memberships(ctx, &MembershipSearchQuery{ - Queries: []SearchQuery{userIDQuery}, + Queries: []SearchQuery{userIDQuery, orgIDsQuery}, }) if err != nil { return nil, err diff --git a/internal/repository/iam/policy_privacy.go b/internal/repository/iam/policy_privacy.go index 4c5fdb3882..8606825149 100644 --- a/internal/repository/iam/policy_privacy.go +++ b/internal/repository/iam/policy_privacy.go @@ -22,7 +22,8 @@ func NewPrivacyPolicyAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, tosLink, - privacyLink string, + privacyLink, + helpLink string, ) *PrivacyPolicyAddedEvent { return &PrivacyPolicyAddedEvent{ PrivacyPolicyAddedEvent: *policy.NewPrivacyPolicyAddedEvent( @@ -31,7 +32,8 @@ func NewPrivacyPolicyAddedEvent( aggregate, PrivacyPolicyAddedEventType), tosLink, - privacyLink), + privacyLink, + helpLink), } } diff --git a/internal/repository/org/policy_privacy.go b/internal/repository/org/policy_privacy.go index dfe1217a80..e28f9d28ea 100644 --- a/internal/repository/org/policy_privacy.go +++ b/internal/repository/org/policy_privacy.go @@ -23,7 +23,8 @@ func NewPrivacyPolicyAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, tosLink, - privacyLink string, + privacyLink, + helpLink string, ) *PrivacyPolicyAddedEvent { return &PrivacyPolicyAddedEvent{ PrivacyPolicyAddedEvent: *policy.NewPrivacyPolicyAddedEvent( @@ -32,7 +33,8 @@ func NewPrivacyPolicyAddedEvent( aggregate, PrivacyPolicyAddedEventType), tosLink, - privacyLink), + privacyLink, + helpLink), } } diff --git a/internal/repository/policy/policy_privacy.go b/internal/repository/policy/policy_privacy.go index b76150c338..36b3b2501c 100644 --- a/internal/repository/policy/policy_privacy.go +++ b/internal/repository/policy/policy_privacy.go @@ -20,6 +20,7 @@ type PrivacyPolicyAddedEvent struct { TOSLink string `json:"tosLink,omitempty"` PrivacyLink string `json:"privacyLink,omitempty"` + HelpLink string `json:"helpLink,omitempty"` } func (e *PrivacyPolicyAddedEvent) Data() interface{} { @@ -33,12 +34,14 @@ func (e *PrivacyPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueC func NewPrivacyPolicyAddedEvent( base *eventstore.BaseEvent, tosLink, - privacyLink string, + privacyLink, + helpLink string, ) *PrivacyPolicyAddedEvent { return &PrivacyPolicyAddedEvent{ BaseEvent: *base, TOSLink: tosLink, PrivacyLink: privacyLink, + HelpLink: helpLink, } } @@ -59,6 +62,7 @@ type PrivacyPolicyChangedEvent struct { TOSLink *string `json:"tosLink,omitempty"` PrivacyLink *string `json:"privacyLink,omitempty"` + HelpLink *string `json:"helpLink,omitempty"` } func (e *PrivacyPolicyChangedEvent) Data() interface{} { @@ -99,6 +103,12 @@ func ChangePrivacyLink(privacyLink string) func(*PrivacyPolicyChangedEvent) { } } +func ChangeHelpLink(helpLink string) func(*PrivacyPolicyChangedEvent) { + return func(e *PrivacyPolicyChangedEvent) { + e.HelpLink = &helpLink + } +} + func PrivacyPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) { e := &PrivacyPolicyChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 5eda4ff579..13db35a19e 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -193,22 +193,22 @@ Errors: NotChanged: Default Message Text wurde nicht verändert AlreadyExists: Default Message Text existiert bereits Invalid: Default Message Text ist ungültig - PasswordComplexity: + PasswordComplexityPolicy: NotFound: Password Komplexitäts Policy konnte nicht gefunden werden Empty: Passwort Komplexitäts Policy ist leer NotExisting: Passwort Komplexitäts Policy existiert nicht AlreadyExists: Passwort Komplexitäts Policy existiert bereits - PasswordLockout: + PasswordLockoutPolicy: NotFound: Password Lockout Policy konnte nicht gefunden werden Empty: Passwort Lockout Policy ist leer NotExisting: Passwort Lockout Policy existiert nicht AlreadyExists: Passwort Lockout Policy existiert bereits - PasswordAge: + PasswordAgePolicy: NotFound: Password Age Policy konnte nicht gefunden werden Empty: Passwort Age Policy ist leer NotExisting: Passwort Age Policy existiert nicht AlreadyExists: Passwort Age Policy existiert bereits - OrgIAM: + OrgIAMPolicy: Empty: Org IAM Policy ist leer NotExisting: Org IAM Policy existiert nicht AlreadyExists: Org IAM Policy existiert bereits @@ -589,6 +589,10 @@ EventTypes: cascade: removed: Berechtigung entfernt changed: Berechtigung geändert + metadata: + set: Benutzer Metadaten gesetzt + removed: Benutzer Metadaten gelöscht + removed.all: Alle Benutzer Metadaten gelöscht org: added: Organisation hinzugefügt changed: Organisation geändert diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 0c4961f656..72ae6a8cf5 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -193,22 +193,22 @@ Errors: NotChanged: Default Message Text has not been changed AlreadyExists: Default Message Text already exists Invalid: Default Message Text is invalid - PasswordComplexity: + PasswordComplexityPolicy: NotFound: Password Complexity Policy not found Empty: Password Complexity Policy is empty NotExisting: Password Complexity Policy doesn't exist AlreadyExists: Password Complexity Policy already exists - PasswordLockout: + PasswordLockoutPolicy: NotFound: Password Lockout Policy not found Empty: Password Lockout Policy is empty NotExisting: Password Lockout Policy doesn't exist AlreadyExists: Password Lockout Policy already exists - PasswordAge: + PasswordAgePolicy: NotFound: Password Age Policy not found Empty: Password Age Policy is empty NotExisting: Password Age Policy doesn't exist AlreadyExists: Password Age Policy already exists - OrgIAM: + OrgIAMPolicy: Empty: Org IAM Policy is empty NotExisting: Org IAM Policy doesn't exist AlreadyExists: Org IAM Policy already exists @@ -589,6 +589,10 @@ EventTypes: cascade: removed: Authorization removed changed: Authorization changed + metadata: + set: User metadata set + removed: User metadata removed + removed.all: All user metadata removed org: added: Organization added changed: Organization changed diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index 9963483a96..ab1684649d 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -191,22 +191,22 @@ Errors: NotChanged: Il testo predefinito non è stato cambiato AlreadyExists: Il testo predefinito già eistente Invalid: Il testo predefinito non è valido - PasswordComplexity: + PasswordComplexityPolicy: NotFound: Impostazioni di complessità della password non trovati Empty: Mancano le impostazioni di complessità della password NotExisting: Impostazioni di complessità della password non esistenti AlreadyExists: Impostazioni di complessità della password sono già esistenti - PasswordLockout: + PasswordLockoutPolicy: NotFound: Impostazioni di blocco della password non trovati Empty: Mancano le impostazioni di blocco della password NotExisting: Le impostazioni di blocco della password non esistenti AlreadyExists: Le impostazioni di blocco della password sono già esistenti - PasswordAge: + PasswordAgePolicy: NotFound: Impostazioni di validità della password Empty: Impostazioni di validità della password mancanti NotExisting: Impostazioni di validità della password non esistenti AlreadyExists: Impostazioni di validità della password sono già esistenti - OrgIAM: + OrgIAMPolicy: Empty: Mancano le impostazioni Org IAM NotExisting: Impostazioni Org IAM non esistenti AlreadyExists: Impostazioni Org IAM già esistenti @@ -587,6 +587,10 @@ EventTypes: cascade: removed: Autorizzazione rimossa changed: Autorizzazione cambiata + metadata: + set: Set di metadati utente + removed: Metadati utente rimossi + removed.all: Tutti i metadati utente rimossi org: added: Organizzazione aggiunta changed: Organizzazione cambiata diff --git a/internal/user/repository/view/model/notify_user.go b/internal/user/repository/view/model/notify_user.go index 1171c3a943..79a23bda1a 100644 --- a/internal/user/repository/view/model/notify_user.go +++ b/internal/user/repository/view/model/notify_user.go @@ -4,8 +4,6 @@ import ( "encoding/json" "time" - "github.com/caos/zitadel/internal/query" - "github.com/caos/logging" "github.com/lib/pq" @@ -100,14 +98,14 @@ func (u *NotifyUser) GenerateLoginName(domain string, appendDomain bool) string return u.UserName + "@" + domain } -func (u *NotifyUser) SetLoginNames(policy *query.OrgIAMPolicy, domains []*org_model.OrgDomain) { +func (u *NotifyUser) SetLoginNames(userLoginMustBeDomain bool, domains []*org_model.OrgDomain) { loginNames := make([]string, 0) for _, d := range domains { if d.Verified { loginNames = append(loginNames, u.GenerateLoginName(d.Domain, true)) } } - if !policy.UserLoginMustBeDomain { + if !userLoginMustBeDomain { loginNames = append(loginNames, u.UserName) } u.LoginNames = loginNames diff --git a/internal/user/repository/view/model/user.go b/internal/user/repository/view/model/user.go index 18b615fc05..672e3f5edd 100644 --- a/internal/user/repository/view/model/user.go +++ b/internal/user/repository/view/model/user.go @@ -8,8 +8,6 @@ import ( "github.com/caos/logging" "github.com/lib/pq" - "github.com/caos/zitadel/internal/query" - req_model "github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" @@ -230,14 +228,14 @@ func (u *UserView) GenerateLoginName(domain string, appendDomain bool) string { return u.UserName + "@" + domain } -func (u *UserView) SetLoginNames(policy *query.OrgIAMPolicy, domains []*org_model.OrgDomain) { +func (u *UserView) SetLoginNames(userLoginMustBeDomain bool, domains []*org_model.OrgDomain) { loginNames := make([]string, 0) for _, d := range domains { if d.Verified { loginNames = append(loginNames, u.GenerateLoginName(d.Domain, true)) } } - if !policy.UserLoginMustBeDomain { + if !userLoginMustBeDomain { loginNames = append(loginNames, u.UserName) } u.LoginNames = loginNames diff --git a/internal/webauthn/webauthn.go b/internal/webauthn/webauthn.go index 0e627c74fb..be70d8a719 100644 --- a/internal/webauthn/webauthn.go +++ b/internal/webauthn/webauthn.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" + "github.com/caos/logging" "github.com/duo-labs/webauthn/protocol" "github.com/duo-labs/webauthn/webauthn" @@ -107,6 +108,8 @@ func (w *WebAuthN) FinishRegistration(user *domain.Human, webAuthN *domain.WebAu } credentialData, err := protocol.ParseCredentialCreationResponseBody(bytes.NewReader(credData)) if err != nil { + e := *err.(*protocol.Error) + logging.WithFields("error", e).Error("webauthn credential could not be parsed") return nil, caos_errs.ThrowInternal(err, "WEBAU-sEr8c", "Errors.User.WebAuthN.ErrorOnParseCredential") } sessionData := WebAuthNToSessionData(webAuthN) diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 2090101353..a390816a4f 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -1771,6 +1771,7 @@ service AdminService { //Updates the default privacy policy of ZITADEL // it impacts all organisations without a customised policy + // Variable {{.Lang}} can be set to have different links based on the language rpc UpdatePrivacyPolicy(UpdatePrivacyPolicyRequest) returns (UpdatePrivacyPolicyResponse) { option (google.api.http) = { put: "/policies/privacy"; @@ -3765,6 +3766,7 @@ message GetPrivacyPolicyResponse { message UpdatePrivacyPolicyRequest { string tos_link = 1; string privacy_link = 2; + string help_link = 3; } message UpdatePrivacyPolicyResponse { diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 3be4a3ed61..92eee5d352 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -2133,6 +2133,7 @@ service ManagementService { // Add a custom privacy policy for the organisation // With this policy privacy relevant things can be configured (e.g. tos link) + // Variable {{.Lang}} can be set to have different links based on the language rpc AddCustomPrivacyPolicy(AddCustomPrivacyPolicyRequest) returns (AddCustomPrivacyPolicyResponse) { option (google.api.http) = { post: "/policies/privacy" @@ -2147,6 +2148,7 @@ service ManagementService { // Update the privacy complexity policy for the organisation // With this policy privacy relevant things can be configured (e.g. tos link) + // Variable {{.Lang}} can be set to have different links based on the language rpc UpdateCustomPrivacyPolicy(UpdateCustomPrivacyPolicyRequest) returns (UpdateCustomPrivacyPolicyResponse) { option (google.api.http) = { put: "/policies/privacy" @@ -4646,6 +4648,7 @@ message GetDefaultPrivacyPolicyResponse { message AddCustomPrivacyPolicyRequest { string tos_link = 1; string privacy_link = 2; + string help_link = 3; } message AddCustomPrivacyPolicyResponse { @@ -4655,6 +4658,7 @@ message AddCustomPrivacyPolicyResponse { message UpdateCustomPrivacyPolicyRequest { string tos_link = 1; string privacy_link = 2; + string help_link = 3; } message UpdateCustomPrivacyPolicyResponse { diff --git a/proto/zitadel/policy.proto b/proto/zitadel/policy.proto index b4f9f75ff5..cbe5090b0c 100644 --- a/proto/zitadel/policy.proto +++ b/proto/zitadel/policy.proto @@ -229,4 +229,5 @@ message PrivacyPolicy { string tos_link = 2; string privacy_link = 3; bool is_default = 4; -} \ No newline at end of file + string help_link = 5; +} diff --git a/proto/zitadel/text.proto b/proto/zitadel/text.proto index a4d9c15c71..03c29f658a 100644 --- a/proto/zitadel/text.proto +++ b/proto/zitadel/text.proto @@ -382,12 +382,11 @@ message LogoutDoneScreenText { } message FooterText { - reserved 2, 4; - reserved "tos_link", "privacy_policy_link"; + reserved 2, 4, 6; + reserved "tos_link", "privacy_policy_link", "help_link"; string tos = 1 [(validate.rules).string = {max_len: 200}]; string privacy_policy = 3 [(validate.rules).string = {max_len: 200}]; string help = 5 [(validate.rules).string = {max_len: 200}]; - string help_link = 6 [(validate.rules).string = {max_len: 500}]; } message PasswordlessPromptScreenText { diff --git a/proto/zitadel/user.proto b/proto/zitadel/user.proto index faa811a1f9..82ebaed89e 100644 --- a/proto/zitadel/user.proto +++ b/proto/zitadel/user.proto @@ -681,6 +681,11 @@ message UserGrant { example: "\"https://api.zitadel.ch/assets/v1/avatar-32432jkh4kj32\""; } ]; + string preferred_login_name = 18 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"gigi@caos.ch\""; + } + ]; } enum UserGrantState {